@geraldmaron/construct 1.2.1 → 1.2.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 +6 -6
- package/bin/construct +41 -67
- package/commands/plan/decide.md +1 -1
- package/commands/remember/runbook.md +1 -1
- package/examples/README.md +1 -1
- package/examples/distribution/README.md +1 -1
- package/examples/distribution/sources/adr.md +2 -2
- package/examples/distribution/sources/deck-one-pager.md +1 -1
- package/examples/distribution/sources/prd-platform.md +1 -1
- package/examples/distribution/sources/research-brief.md +2 -2
- package/examples/distribution/sources/rfc-platform.md +1 -1
- package/examples/distribution/sources/runbook.md +2 -2
- package/examples/distribution/sources/strategy.md +1 -1
- package/lib/agent-instructions/inject.mjs +1 -1
- package/lib/artifact-type-from-path.mjs +9 -5
- package/lib/audit-specialists.mjs +1 -1
- package/lib/audit-trail.mjs +3 -3
- package/lib/auto-docs.mjs +10 -10
- package/lib/bootstrap/built-ins.mjs +3 -3
- package/lib/boundary.mjs +3 -2
- package/lib/brand-prose.mjs +1 -1
- package/lib/cache-governor.js +4 -4
- package/lib/certification/document-io-fixtures.mjs +1 -1
- package/lib/chat/config.mjs +2 -2
- package/lib/chat/desktop-binary.mjs +5 -3
- package/lib/cli-commands.mjs +3 -3
- package/lib/cli-service-inventory.mjs +1 -1
- package/lib/comment-lint.mjs +9 -5
- package/lib/config/alias.mjs +3 -2
- package/lib/config/intake-policy.mjs +8 -78
- package/lib/config/project-config.mjs +95 -5
- package/lib/config/schema.mjs +9 -19
- package/lib/config/xdg.mjs +63 -0
- package/lib/contracts/violation-log.mjs +2 -2
- package/lib/cost-ledger.mjs +4 -3
- package/lib/cost.mjs +3 -2
- package/lib/decisions/registry.mjs +3 -3
- package/lib/distill.mjs +2 -2
- package/lib/docs-verify.mjs +2 -0
- package/lib/doctor/audit.mjs +4 -3
- package/lib/doctor/cli.mjs +0 -1
- package/lib/doctor/index.mjs +4 -4
- package/lib/doctor/report.mjs +3 -3
- package/lib/doctor/watchers/bd-watch.mjs +5 -5
- package/lib/doctor/watchers/cost.mjs +4 -4
- package/lib/doctor/watchers/disk.mjs +4 -3
- package/lib/doctor/watchers/service-health.mjs +5 -5
- package/lib/document-export.mjs +1 -1
- package/lib/document-extract.mjs +3 -3
- package/lib/document-ingest.mjs +13 -3
- package/lib/efficiency.mjs +2 -1
- package/lib/embed/approval-queue.mjs +3 -2
- package/lib/embed/cli.mjs +7 -5
- package/lib/embed/config.mjs +6 -5
- package/lib/embed/daemon.mjs +6 -5
- package/lib/embed/inbox-live-watcher.mjs +1 -1
- package/lib/embed/inbox.mjs +52 -37
- package/lib/embed/intake-metrics.mjs +3 -2
- package/lib/embed/reconcile.mjs +1 -1
- package/lib/embed/semantic.mjs +3 -2
- package/lib/embed/supervision.mjs +4 -2
- package/lib/engine/registry.mjs +3 -3
- package/lib/env-config.mjs +34 -12
- package/lib/features.mjs +4 -3
- package/lib/health-check.mjs +7 -6
- package/lib/hook-health.mjs +5 -4
- package/lib/hooks/_lib/log.mjs +4 -3
- package/lib/hooks/_lib/output-mode.mjs +2 -1
- package/lib/hooks/agent-tracker.mjs +3 -4
- package/lib/hooks/audit-reads.mjs +2 -2
- package/lib/hooks/audit-trail.mjs +2 -2
- package/lib/hooks/bash-output-logger.mjs +3 -3
- package/lib/hooks/brand-prose-lint.mjs +1 -1
- package/lib/hooks/ci-status-check.mjs +3 -2
- package/lib/hooks/context-watch.mjs +2 -2
- package/lib/hooks/context-window-recovery.mjs +5 -5
- package/lib/hooks/dep-audit.mjs +2 -2
- package/lib/hooks/doc-coupling-check.mjs +3 -2
- package/lib/hooks/edit-accumulator.mjs +3 -2
- package/lib/hooks/edit-guard.mjs +3 -3
- package/lib/hooks/guard-bash.mjs +2 -2
- package/lib/hooks/model-fallback.mjs +2 -1
- package/lib/hooks/policy-engine.mjs +4 -3
- package/lib/hooks/pre-compact.mjs +7 -7
- package/lib/hooks/readme-age-check.mjs +2 -2
- package/lib/hooks/session-optimize.mjs +4 -3
- package/lib/hooks/session-start.mjs +6 -5
- package/lib/hooks/stop-notify.mjs +9 -8
- package/lib/hooks/stop-typecheck.mjs +4 -3
- package/lib/hooks/test-watch.mjs +2 -2
- package/lib/host-disposition.mjs +1 -1
- package/lib/ingest/provider-extract.mjs +2 -1
- package/lib/init/detect-existing-structure.mjs +2 -2
- package/lib/init/doc-lanes.mjs +3 -10
- package/lib/init-docs.mjs +12 -54
- package/lib/init-unified.mjs +35 -81
- package/lib/init.mjs +7 -5
- package/lib/install/desktop-binary-download.mjs +5 -2
- package/lib/intake/daemon.mjs +31 -8
- package/lib/intake/intake-config.mjs +5 -32
- package/lib/integrations/intake-integrations.mjs +8 -7
- package/lib/knowledge/rag.mjs +2 -2
- package/lib/knowledge/search.mjs +15 -15
- package/lib/maintenance/cleanup.mjs +14 -10
- package/lib/mcp/server.mjs +1 -1
- package/lib/mcp/tools/project.mjs +2 -1
- package/lib/model-cheapest-provider.mjs +4 -3
- package/lib/model-pricing.mjs +3 -2
- package/lib/model-router.mjs +4 -3
- package/lib/models/catalog.mjs +2 -1
- package/lib/models/execution-capability-profile.mjs +2 -1
- package/lib/models/provider-poll.mjs +2 -1
- package/lib/ollama-manager.mjs +0 -1
- package/lib/op-log.mjs +2 -1
- package/lib/opencode-runtime-plugin.mjs +3 -2
- package/lib/oracle/artifact-gate.mjs +3 -0
- package/lib/oracle/cli.mjs +2 -1
- package/lib/oracle/execute.mjs +3 -2
- package/lib/oracle/index.mjs +2 -1
- package/lib/oracle/read-model.mjs +3 -2
- package/lib/performance/generate.mjs +4 -3
- package/lib/platforms/capabilities.mjs +1 -1
- package/lib/plugin-registry.mjs +2 -1
- package/lib/profiles/lifecycle.mjs +3 -3
- package/lib/project-profile.mjs +2 -1
- package/lib/project-root.mjs +9 -7
- package/lib/provider-capabilities.js +4 -3
- package/lib/providers/auth-manager.mjs +2 -1
- package/lib/providers/copilot-auth.mjs +3 -2
- package/lib/providers/creds.mjs +3 -2
- package/lib/providers/registry.mjs +3 -3
- package/lib/providers/secret-resolver.mjs +3 -2
- package/lib/read-tracker-store.mjs +2 -1
- package/lib/reconcile/mcp-entry-reconcile.mjs +2 -2
- package/lib/reflect.mjs +2 -1
- package/lib/registry/generate-docs.mjs +2 -2
- package/lib/roles/approval-surface.mjs +5 -4
- package/lib/roles/event-bus.mjs +0 -1
- package/lib/roles/gateway.mjs +6 -2
- package/lib/rules-delivery.mjs +1 -1
- package/lib/runtime-pressure.mjs +5 -3
- package/lib/sandbox.mjs +3 -2
- package/lib/scheduler/solo.mjs +6 -4
- package/lib/server/auth.mjs +4 -3
- package/lib/server/index.mjs +24 -26
- package/lib/server/insights.mjs +5 -4
- package/lib/server/webhook.mjs +2 -1
- package/lib/service-manager.mjs +5 -3
- package/lib/setup.mjs +9 -8
- package/lib/status.mjs +6 -5
- package/lib/storage/embeddings-local.mjs +3 -2
- package/lib/storage/sync.mjs +2 -2
- package/lib/telemetry/client.mjs +0 -1
- package/lib/telemetry/intent-verifications.mjs +6 -6
- package/lib/telemetry/model-pricing-catalog.mjs +4 -3
- package/lib/telemetry/rule-calls.mjs +3 -3
- package/lib/telemetry/skill-calls.mjs +3 -3
- package/lib/template-registry.mjs +1 -0
- package/lib/templates/visual-requirements.mjs +1 -1
- package/lib/test-corpus-inventory.mjs +1 -1
- package/lib/uninstall/uninstall.mjs +12 -10
- package/package.json +3 -3
- package/platforms/claude/settings.template.json +43 -43
- package/rules/common/no-fabrication.md +1 -1
- package/rules/common/release-gates.md +1 -1
- package/rules/common/research.md +1 -1
- package/rules/common/review-before-change.md +1 -1
- package/scripts/sync-specialists.mjs +11 -8
- package/skills/docs/adr-workflow.md +3 -3
- package/skills/docs/init-docs.md +9 -9
- package/skills/docs/prd-workflow.md +5 -5
- package/skills/docs/product-intelligence-review.md +1 -1
- package/skills/docs/product-intelligence-workflow.md +2 -2
- package/skills/docs/product-signal-workflow.md +1 -1
- package/skills/docs/runbook-workflow.md +4 -4
- package/skills/exploration/unknown-codebase-onboarding.md +1 -1
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/routing.md +3 -3
- package/specialists/prompts/cx-architect.md +1 -1
- package/specialists/prompts/cx-docs-keeper.md +1 -1
- package/specialists/prompts/cx-researcher.md +1 -1
- package/specialists/prompts/cx-sre.md +1 -1
- package/specialists/role-manifests.json +6 -6
- package/templates/docs/README.md +125 -0
- package/templates/docs/construct_guide.md +3 -4
- package/templates/docs/persona-artifact.md +1 -1
- package/templates/docs/prds/README.md +25 -0
- package/templates/docs/prds/templates/_template.md +206 -0
- package/templates/docs/prds/templates/meta-prd.template.md +177 -0
- package/templates/docs/prds/templates/prd-business.template.md +61 -0
- package/templates/docs/prds/templates/prd-platform.template.md +81 -0
- package/templates/docs/prds/templates/prfaq.template.md +38 -0
- package/templates/docs/rfcs/README.md +22 -0
- package/templates/docs/rfcs/templates/_template.md +58 -0
- package/templates/docs/rfcs/templates/rfc-platform.template.md +72 -0
- package/templates/homebrew/construct.rb +1 -1
- package/templates/workflows/new-feature.yml +8 -8
- package/lib/intake/legacy-paths.mjs +0 -5
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Bootstrap local services (once per machine, opt-in to machine-scope writes):
|
|
|
28
28
|
construct install --scope=user --yes
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
`construct install` defaults to `--scope=project`, which writes nothing and prints scope guidance — see the [footprint contract](#footprint-contract) below or [ADR 0029](docs/adr/0029-install-scopes-and-hook-budgets.md). Use `--scope=user` for machine setup, `--scope=both` for both.
|
|
31
|
+
`construct install` defaults to `--scope=project`, which writes nothing and prints scope guidance — see the [footprint contract](#footprint-contract) below or [ADR 0029](docs/decisions/adr/0029-install-scopes-and-hook-budgets.md). Use `--scope=user` for machine setup, `--scope=both` for both.
|
|
32
32
|
|
|
33
33
|
Initialize a project:
|
|
34
34
|
|
|
@@ -87,7 +87,7 @@ Pick or change modes with `construct config mode [solo|team|enterprise]`. [Deplo
|
|
|
87
87
|
|
|
88
88
|
## Intake
|
|
89
89
|
|
|
90
|
-
Anything dropped into
|
|
90
|
+
Anything dropped into `inbox/` (a bug report, a customer comment, a competitor PDF, a postmortem draft) is classified by the active profile's intake taxonomy. The default `rnd` profile uses bug, user-signal, experiment, architecture, incident, security, requirement, research, ops, eval-finding, launch-asset, legal-compliance. The `operations` profile uses request, incident, ops, security, docs. The `creative` profile uses brief, content-request, asset, experiment, report. The `research` profile uses question, study, synthesis, report.
|
|
91
91
|
|
|
92
92
|
Each signal gets a primary owner and a recommended handoff chain. Inspect with `construct intake list` and `construct intake show <id>`. Generate a task graph with `construct graph from-intake <id>`. The classifier runs in the daemon and is deterministic. The agent in your editor does the actual analysis. [Intake and triage](https://geraldmaron.github.io/construct/concepts/intake-and-triage).
|
|
93
93
|
|
|
@@ -111,11 +111,11 @@ Construct's writes are scoped and disclosed up front. The default `construct ins
|
|
|
111
111
|
| Machine | `construct install --scope=user` | `~/.construct/config.env`, `~/.construct/lib` (symlink), `~/.construct/services/`, `~/Library/LaunchAgents/` (macOS), MCP entries in `~/.config/opencode/opencode.json` and `~/.codex/config.toml`, marker block in `~/.claude/CLAUDE.md`, hook injection in `~/.claude/settings.json` (last two require interactive consent or `--yes`) |
|
|
112
112
|
| Never touched | — | Shell rc files (`~/.bashrc`, `~/.zshrc`), npm global config, `git config --global` |
|
|
113
113
|
|
|
114
|
-
Full table with file:line citations and the per-hook performance budget contract: [Architecture — Footprint contract](https://geraldmaron.github.io/construct/concepts/architecture#footprint-contract) and [ADR 0029](docs/adr/0029-install-scopes-and-hook-budgets.md).
|
|
114
|
+
Full table with file:line citations and the per-hook performance budget contract: [Architecture — Footprint contract](https://geraldmaron.github.io/construct/concepts/architecture#footprint-contract) and [ADR 0029](docs/decisions/adr/0029-install-scopes-and-hook-budgets.md).
|
|
115
115
|
|
|
116
116
|
## Learning loops
|
|
117
117
|
|
|
118
|
-
Construct gets smarter on its own. Every session ends with an automatic capture: tools used, files touched, what the final reply said. That goes into `.cx/observations/` and is searchable from the next session. See [`docs/concepts/learning-loops.mdx`](./docs/concepts/learning-loops.mdx) for what's wired, what's coming, and how to turn pieces off.
|
|
118
|
+
Construct gets smarter on its own. Every session ends with an automatic capture: tools used, files touched, what the final reply said. That goes into `.cx/observations/` and is searchable from the next session. See [`docs/guides/concepts/learning-loops.mdx`](./docs/guides/concepts/learning-loops.mdx) for what's wired, what's coming, and how to turn pieces off.
|
|
119
119
|
|
|
120
120
|
## `.cx/` is local-only runtime state
|
|
121
121
|
|
|
@@ -226,7 +226,7 @@ The embed daemon writes its supervisor stdout log to `~/.cx/runtime/embed-daemon
|
|
|
226
226
|
| `construct doc` | Verify or inspect auditability stamps on Construct-generated markdown files |
|
|
227
227
|
| `construct docs:check` | Check for missing how-to guides (alias for `docs check`) |
|
|
228
228
|
| `construct docs:reconcile` | Reconcile docs against the registry |
|
|
229
|
-
| `construct docs:site` | Regenerate generated reference pages under docs/reference/ |
|
|
229
|
+
| `construct docs:site` | Regenerate generated reference pages under docs/guides/reference/ |
|
|
230
230
|
| `construct docs:update` | Regenerate AUTO-managed doc regions (alias for `docs update`) |
|
|
231
231
|
| `construct docs:verify` | Validate documentation quality (alias for `docs verify`) |
|
|
232
232
|
| `construct impact` | Change-impact analysis — map changed files to affected tests, capabilities, and workflows |
|
|
@@ -269,7 +269,7 @@ The embed daemon writes its supervisor stdout log to `~/.cx/runtime/embed-daemon
|
|
|
269
269
|
|
|
270
270
|
- [`CONTRIBUTING.md`](./CONTRIBUTING.md). Branch workflow, gates, review expectations.
|
|
271
271
|
- [`CHANGELOG.md`](./CHANGELOG.md). Release history.
|
|
272
|
-
- [`docs/concepts/architecture.mdx`](./docs/concepts/architecture.mdx). Canonical architecture.
|
|
272
|
+
- [`docs/guides/concepts/architecture.mdx`](./docs/guides/concepts/architecture.mdx). Canonical architecture.
|
|
273
273
|
- [`AGENTS.md`](./AGENTS.md). Agent operating contract.
|
|
274
274
|
|
|
275
275
|
## Project structure
|
package/bin/construct
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
PROJECT_CONFIG_FILENAME,
|
|
39
39
|
} from '../lib/config/project-config.mjs';
|
|
40
40
|
import { validateProjectConfig } from '../lib/config/schema.mjs';
|
|
41
|
+
import { configDir, doctorRoot } from '../lib/config/xdg.mjs';
|
|
41
42
|
import {
|
|
42
43
|
DEPLOYMENT_MODES,
|
|
43
44
|
DEPLOYMENT_MODE_ENV_KEY,
|
|
@@ -400,7 +401,7 @@ async function cmdDoctor() {
|
|
|
400
401
|
}
|
|
401
402
|
|
|
402
403
|
// .env files
|
|
403
|
-
for (const envPath of [join(process.cwd(), '.env'), join(homedir(), '.env'),
|
|
404
|
+
for (const envPath of [join(process.cwd(), '.env'), join(homedir(), '.env'), getUserEnvPath(homedir())]) {
|
|
404
405
|
if (fs.existsSync(envPath)) {
|
|
405
406
|
try {
|
|
406
407
|
const content = fs.readFileSync(envPath, 'utf8');
|
|
@@ -592,7 +593,7 @@ async function cmdDoctor() {
|
|
|
592
593
|
// minute at 50MB, but a freshly-upgraded install with a 34GB legacy
|
|
593
594
|
// log shouldn't wait 60s. --fix-embed-log forces rotation now.
|
|
594
595
|
|
|
595
|
-
const embedLogPath = path.join(userHome, '
|
|
596
|
+
const embedLogPath = path.join(doctorRoot(userHome), 'runtime', 'embed-daemon.log');
|
|
596
597
|
if (fs.existsSync(embedLogPath)) {
|
|
597
598
|
const sizeMb = fs.statSync(embedLogPath).size / 1024 / 1024;
|
|
598
599
|
const FORCE_THRESHOLD_MB = Number(process.env.CONSTRUCT_DOCTOR_EMBED_LOG_FORCE_MB) || 500;
|
|
@@ -627,11 +628,11 @@ async function cmdDoctor() {
|
|
|
627
628
|
'intent-verifications.jsonl',
|
|
628
629
|
];
|
|
629
630
|
const legacyState = projectScopedNames
|
|
630
|
-
.map((name) => ({ name, path: path.join(userHome,
|
|
631
|
+
.map((name) => ({ name, path: path.join(doctorRoot(userHome), name) }))
|
|
631
632
|
.filter((p) => fs.existsSync(p.path) && fs.statSync(p.path).size > 0);
|
|
632
633
|
|
|
633
634
|
if (legacyState.length > 0 && wantsFixMigrateState) {
|
|
634
|
-
const archiveDir = path.join(userHome, '
|
|
635
|
+
const archiveDir = path.join(doctorRoot(userHome), 'legacy');
|
|
635
636
|
fs.mkdirSync(archiveDir, { recursive: true });
|
|
636
637
|
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
637
638
|
const moved = [];
|
|
@@ -739,13 +740,13 @@ async function cmdDoctor() {
|
|
|
739
740
|
const vscodeSettingsOk = vscodeSettingsPaths.some((candidate) => fs.existsSync(candidate));
|
|
740
741
|
if (vscodeSettingsOk) add('VS Code settings file', true, true);
|
|
741
742
|
}
|
|
742
|
-
add('User config ready', fs.existsSync(getUserEnvPath(HOME)) || fs.existsSync(
|
|
743
|
+
add('User config ready', fs.existsSync(getUserEnvPath(HOME)) || fs.existsSync(configDir(HOME)), true);
|
|
743
744
|
add('skills/ directory', fs.existsSync(path.join(ROOT_DIR, 'skills')));
|
|
744
745
|
add('rules/common/', fs.existsSync(path.join(ROOT_DIR, 'rules', 'common')));
|
|
745
746
|
add('specialists/prompts/ directory', fs.existsSync(path.join(ROOT_DIR, 'specialists', 'prompts')));
|
|
746
747
|
add('Hybrid storage env keys documented', fs.existsSync(path.join(ROOT_DIR, '.env.example')));
|
|
747
|
-
fs.mkdirSync(path.join(
|
|
748
|
-
add('
|
|
748
|
+
fs.mkdirSync(path.join(doctorRoot(), 'performance-reviews'), { recursive: true });
|
|
749
|
+
add('performance-reviews/ ready', true);
|
|
749
750
|
|
|
750
751
|
// construct.config.json — optional today (config is permitted to be absent;
|
|
751
752
|
// env vars + defaults still work). The check enforces that when the file
|
|
@@ -765,33 +766,29 @@ async function cmdDoctor() {
|
|
|
765
766
|
|
|
766
767
|
// Claude Code Stop-hook installation check. The settings template
|
|
767
768
|
// wires several Stop hooks (policy-engine / stop-typecheck /
|
|
768
|
-
// stop-notify / readme-age-check)
|
|
769
|
-
//
|
|
770
|
-
//
|
|
771
|
-
//
|
|
772
|
-
//
|
|
773
|
-
// command away.
|
|
769
|
+
// stop-notify / readme-age-check) whose commands sync resolves to the
|
|
770
|
+
// hook lib symlink. If those scripts don't resolve, every Stop hook
|
|
771
|
+
// fails silently — including stop-notify, which is what appends session
|
|
772
|
+
// cost data. Result: the dashboard stops updating and the user sees stale
|
|
773
|
+
// data. Surface loudly so the fix is one command away.
|
|
774
774
|
try {
|
|
775
775
|
const settingsPath = path.join(HOME, '.claude', 'settings.json');
|
|
776
776
|
if (fs.existsSync(settingsPath)) {
|
|
777
777
|
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
778
778
|
const stopHooks = settings?.hooks?.Stop || [];
|
|
779
|
-
const installRoot = path.join(HOME, '
|
|
780
|
-
const installed = fs.existsSync(installRoot);
|
|
779
|
+
const installRoot = path.join(configDir(HOME), 'lib', 'hooks');
|
|
781
780
|
const referenced = [];
|
|
782
781
|
for (const group of stopHooks) {
|
|
783
782
|
for (const h of (group?.hooks || [])) {
|
|
784
783
|
const cmd = String(h?.command || '');
|
|
785
|
-
const m = cmd.match(
|
|
784
|
+
const m = cmd.match(/(?:^|["\s])(\/[^"\s]*\/lib\/hooks\/[a-zA-Z0-9_-]+\.mjs)/);
|
|
786
785
|
if (m) referenced.push(m[1]);
|
|
787
786
|
}
|
|
788
787
|
}
|
|
789
|
-
const missing =
|
|
790
|
-
? referenced.filter((f) => !fs.existsSync(path.join(installRoot, f)))
|
|
791
|
-
: referenced;
|
|
788
|
+
const missing = referenced.filter((scriptPath) => !fs.existsSync(scriptPath));
|
|
792
789
|
if (referenced.length > 0 && missing.length > 0) {
|
|
793
790
|
add(
|
|
794
|
-
`Claude Stop hooks broken: ${missing.length} hook${missing.length === 1 ? '' : 's'}
|
|
791
|
+
`Claude Stop hooks broken: ${missing.length} hook${missing.length === 1 ? '' : 's'} do not resolve. Re-run \`construct install --scope=user\` (symlinks ${ROOT_DIR}/lib → ${installRoot.replace(/\/hooks$/, '')}).`,
|
|
795
792
|
false,
|
|
796
793
|
false,
|
|
797
794
|
);
|
|
@@ -1073,7 +1070,7 @@ async function cmdDoctor() {
|
|
|
1073
1070
|
}
|
|
1074
1071
|
|
|
1075
1072
|
try {
|
|
1076
|
-
const failuresPath = path.join(
|
|
1073
|
+
const failuresPath = path.join(doctorRoot(), 'hook-failures.jsonl');
|
|
1077
1074
|
if (fs.existsSync(failuresPath)) {
|
|
1078
1075
|
const lines = fs.readFileSync(failuresPath, 'utf8').trim().split('\n').filter(Boolean);
|
|
1079
1076
|
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
@@ -1519,7 +1516,7 @@ async function cmdMigrate(args) {
|
|
|
1519
1516
|
}
|
|
1520
1517
|
|
|
1521
1518
|
async function cmdReview(args) {
|
|
1522
|
-
fs.mkdirSync(path.join(
|
|
1519
|
+
fs.mkdirSync(path.join(doctorRoot(), 'performance-reviews'), { recursive: true });
|
|
1523
1520
|
const sub = args[0];
|
|
1524
1521
|
|
|
1525
1522
|
if (!sub || sub === 'run') {
|
|
@@ -2018,7 +2015,7 @@ async function _cmdPricing(args) {
|
|
|
2018
2015
|
const f = describePricingCatalogFreshness();
|
|
2019
2016
|
if (!f.present) { info(f.message); return; }
|
|
2020
2017
|
try {
|
|
2021
|
-
const cached = JSON.parse(fs.readFileSync(path.join(
|
|
2018
|
+
const cached = JSON.parse(fs.readFileSync(path.join(doctorRoot(), 'pricing-cache.json'), 'utf8'));
|
|
2022
2019
|
const divergences = checkPricingDivergence(cached.models || []);
|
|
2023
2020
|
if (divergences.length === 0) { info('No pricing divergences (>25%) between LiteLLM and the static fallback.'); return; }
|
|
2024
2021
|
println(`⚠ ${divergences.length} divergence(s):`);
|
|
@@ -2116,15 +2113,13 @@ async function printIntakeHelp() {
|
|
|
2116
2113
|
println(' construct intake metrics Show pipeline volume, velocity, and processing stats.');
|
|
2117
2114
|
println(' construct intake integrate <id> <system> Push intake to external system (github, jira, confluence).');
|
|
2118
2115
|
println(' construct intake integrate <id> <system> --create-issue Create an issue from the intake packet.');
|
|
2119
|
-
println(' construct intake config Show
|
|
2116
|
+
println(' construct intake config Show the inbox/ drop zone + scan depth.');
|
|
2120
2117
|
println(' construct intake config set --depth=N [--add-dir=PATH] [--remove-dir=PATH]');
|
|
2118
|
+
println(' Update intake config. --depth caps at the hard limit.');
|
|
2121
2119
|
println(' construct intake extraction-warnings [--since=7d] [--kind=table] [--limit=50]');
|
|
2122
2120
|
println(' Summarize ~/.cx/intake/extraction-warnings.jsonl (dropped content log).');
|
|
2123
2121
|
println(' construct intake needs-asr list List audio/video files awaiting ASR transcription.');
|
|
2124
2122
|
println(' construct intake needs-asr show <id> Show a specific ASR-pending packet.');
|
|
2125
|
-
println(' Update intake config. --depth caps at the hard limit.');
|
|
2126
|
-
println(' construct intake config set --include-project-inbox=on|off');
|
|
2127
|
-
println(' construct intake config set --include-docs-intake=on|off');
|
|
2128
2123
|
}
|
|
2129
2124
|
|
|
2130
2125
|
function parseKvFlags(args, keys) {
|
|
@@ -2618,7 +2613,6 @@ async function cmdIntakeReroute(args, cwd) {
|
|
|
2618
2613
|
|
|
2619
2614
|
async function cmdIntakeExtractionWarnings(args) {
|
|
2620
2615
|
const { createReadStream, existsSync } = await import('node:fs');
|
|
2621
|
-
const { homedir } = await import('node:os');
|
|
2622
2616
|
const { join } = await import('node:path');
|
|
2623
2617
|
const { createInterface } = await import('node:readline');
|
|
2624
2618
|
|
|
@@ -2636,7 +2630,7 @@ async function cmdIntakeExtractionWarnings(args) {
|
|
|
2636
2630
|
}
|
|
2637
2631
|
}
|
|
2638
2632
|
|
|
2639
|
-
const logPath = join(
|
|
2633
|
+
const logPath = join(doctorRoot(), 'intake', 'extraction-warnings.jsonl');
|
|
2640
2634
|
if (!existsSync(logPath)) {
|
|
2641
2635
|
println('No extraction warnings recorded yet.');
|
|
2642
2636
|
return;
|
|
@@ -2687,11 +2681,10 @@ async function cmdIntakeExtractionWarnings(args) {
|
|
|
2687
2681
|
|
|
2688
2682
|
async function cmdIntakeNeedsAsr(args) {
|
|
2689
2683
|
const { existsSync, readdirSync, readFileSync } = await import('node:fs');
|
|
2690
|
-
const { homedir } = await import('node:os');
|
|
2691
2684
|
const { join } = await import('node:path');
|
|
2692
2685
|
|
|
2693
2686
|
const sub = args[0] || 'list';
|
|
2694
|
-
const asrDir = join(
|
|
2687
|
+
const asrDir = join(doctorRoot(), 'intake', 'needs-asr');
|
|
2695
2688
|
|
|
2696
2689
|
if (sub === 'list') {
|
|
2697
2690
|
if (!existsSync(asrDir)) { println('No audio/video files awaiting ASR.'); return; }
|
|
@@ -2856,33 +2849,23 @@ async function cmdIntakeConfig(args) {
|
|
|
2856
2849
|
const cwd = process.cwd();
|
|
2857
2850
|
|
|
2858
2851
|
const action = args[0];
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
if (!result.migrated) {
|
|
2863
|
-
info(result.reason ?? 'nothing to migrate');
|
|
2864
|
-
return;
|
|
2865
|
-
}
|
|
2866
|
-
info('Migrated .cx/intake-config.json → construct.config.json intakePolicy');
|
|
2867
|
-
println(JSON.stringify(result.policy, null, 2));
|
|
2868
|
-
return;
|
|
2869
|
-
}
|
|
2852
|
+
|
|
2853
|
+
// Single-zone model (ADR-0045 §C): the canonical `inbox/` is always watched.
|
|
2854
|
+
// Only maxDepth and additionalDirs are configurable.
|
|
2870
2855
|
|
|
2871
2856
|
if (!action || action === 'show') {
|
|
2872
2857
|
const cfg = loadIntakeConfig(cwd, process.env);
|
|
2873
2858
|
const desc = describeIntakeDepth(cfg.maxDepth);
|
|
2874
2859
|
println(`Intake config (root: ${cwd})`);
|
|
2875
2860
|
println('');
|
|
2876
|
-
println(
|
|
2877
|
-
println(` includeRootInbox: ${(cfg.includeRootInbox ?? cfg.includeArchetypeInbox) ? 'on' : 'off'} (inbox/)`);
|
|
2878
|
-
println(` includeDocsIntake: ${cfg.includeDocsIntake ? 'on' : 'off'} (docs/intake)`);
|
|
2861
|
+
println(' drop zone: inbox/ (always watched)');
|
|
2879
2862
|
println(` maxDepth: ${cfg.maxDepth} — ${desc.label}`);
|
|
2880
2863
|
println(` ${desc.detail}`);
|
|
2881
2864
|
println(` hardMaxDepth: ${INTAKE_HARD_MAX_DEPTH}`);
|
|
2882
2865
|
if (cfg.parentDirs.length === 0) {
|
|
2883
|
-
println('
|
|
2866
|
+
println(' additionalDirs: (none — only inbox/ is watched)');
|
|
2884
2867
|
} else {
|
|
2885
|
-
println('
|
|
2868
|
+
println(' additionalDirs:');
|
|
2886
2869
|
for (const dir of cfg.parentDirs) println(` - ${dir}`);
|
|
2887
2870
|
}
|
|
2888
2871
|
println('');
|
|
@@ -2903,15 +2886,6 @@ async function cmdIntakeConfig(args) {
|
|
|
2903
2886
|
const depthFlag = tail.find((a) => a.startsWith('--depth='));
|
|
2904
2887
|
if (depthFlag) patch.maxDepth = Number(depthFlag.split('=')[1]);
|
|
2905
2888
|
|
|
2906
|
-
const projectFlag = tail.find((a) => a.startsWith('--include-project-inbox='));
|
|
2907
|
-
if (projectFlag) patch.includeProjectInbox = /^(on|true|1|yes)$/i.test(projectFlag.split('=')[1] || '');
|
|
2908
|
-
|
|
2909
|
-
const docsFlag = tail.find((a) => a.startsWith('--include-docs-intake='));
|
|
2910
|
-
if (docsFlag) patch.includeDocsIntake = /^(on|true|1|yes)$/i.test(docsFlag.split('=')[1] || '');
|
|
2911
|
-
|
|
2912
|
-
const rootFlag = tail.find((a) => a.startsWith('--include-root-inbox='));
|
|
2913
|
-
if (rootFlag) patch.includeRootInbox = /^(on|true|1|yes)$/i.test(rootFlag.split('=')[1] || '');
|
|
2914
|
-
|
|
2915
2889
|
const addFlags = tail.filter((a) => a.startsWith('--add-dir='));
|
|
2916
2890
|
const removeFlags = tail.filter((a) => a.startsWith('--remove-dir='));
|
|
2917
2891
|
|
|
@@ -2924,7 +2898,7 @@ async function cmdIntakeConfig(args) {
|
|
|
2924
2898
|
}
|
|
2925
2899
|
|
|
2926
2900
|
if (Object.keys(patch).length === 0) {
|
|
2927
|
-
errorln('Nothing to change. Pass --depth=N, --add-dir=PATH, --remove-dir=PATH
|
|
2901
|
+
errorln('Nothing to change. Pass --depth=N, --add-dir=PATH, or --remove-dir=PATH.');
|
|
2928
2902
|
process.exit(1);
|
|
2929
2903
|
}
|
|
2930
2904
|
|
|
@@ -4034,7 +4008,7 @@ async function cmdPluginEngine(args) {
|
|
|
4034
4008
|
const [action = 'list', ...rest] = args;
|
|
4035
4009
|
const isGlobal = rest.includes('--global');
|
|
4036
4010
|
const targetPath = isGlobal
|
|
4037
|
-
? path.join(HOME, '
|
|
4011
|
+
? path.join(configDir(HOME), 'plugins.json')
|
|
4038
4012
|
: path.join(process.cwd(), '.cx', 'plugins.json');
|
|
4039
4013
|
|
|
4040
4014
|
function readPluginsJson() {
|
|
@@ -4241,7 +4215,7 @@ async function cmdEvals(args) {
|
|
|
4241
4215
|
const report = await evaluateFixture(fixturePath);
|
|
4242
4216
|
|
|
4243
4217
|
try {
|
|
4244
|
-
const cacheDir = path.join(
|
|
4218
|
+
const cacheDir = path.join(doctorRoot(), 'evals');
|
|
4245
4219
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
4246
4220
|
const ts = new Date().toISOString();
|
|
4247
4221
|
fs.writeFileSync(
|
|
@@ -4438,7 +4412,7 @@ async function cmdDocsCheck(args) {
|
|
|
4438
4412
|
println('Commands with no linked how-to in docs/README.md:');
|
|
4439
4413
|
for (const name of result.uncovered) println(` ✗ construct ${name}`);
|
|
4440
4414
|
println('');
|
|
4441
|
-
println(`Add cookbook recipes under docs/cookbook/ and link them from the cookbook index.`);
|
|
4415
|
+
println(`Add cookbook recipes under docs/guides/cookbook/ and link them from the cookbook index.`);
|
|
4442
4416
|
process.exitCode = 1;
|
|
4443
4417
|
}
|
|
4444
4418
|
}
|
|
@@ -4449,7 +4423,7 @@ async function cmdDocsSite(args = []) {
|
|
|
4449
4423
|
if (check) {
|
|
4450
4424
|
const { drift } = checkFumadocsReferenceDrift({ rootDir: ROOT_DIR });
|
|
4451
4425
|
if (drift.length === 0) {
|
|
4452
|
-
ok('docs/reference/ is up to date.');
|
|
4426
|
+
ok('docs/guides/reference/ is up to date.');
|
|
4453
4427
|
return;
|
|
4454
4428
|
}
|
|
4455
4429
|
println('');
|
|
@@ -4460,9 +4434,9 @@ async function cmdDocsSite(args = []) {
|
|
|
4460
4434
|
}
|
|
4461
4435
|
const { written } = buildFumadocsReference({ rootDir: ROOT_DIR });
|
|
4462
4436
|
if (written.length === 0) {
|
|
4463
|
-
ok('docs/reference/ already up to date.');
|
|
4437
|
+
ok('docs/guides/reference/ already up to date.');
|
|
4464
4438
|
} else {
|
|
4465
|
-
ok(`docs/reference/ regenerated (${written.length} file${written.length === 1 ? '' : 's'}).`);
|
|
4439
|
+
ok(`docs/guides/reference/ regenerated (${written.length} file${written.length === 1 ? '' : 's'}).`);
|
|
4466
4440
|
}
|
|
4467
4441
|
}
|
|
4468
4442
|
|
|
@@ -4961,7 +4935,7 @@ async function cmdProvider(args) {
|
|
|
4961
4935
|
if (!id) { errorln(`Usage: construct provider plugins ${action} <id> [<package>]`); process.exit(1); }
|
|
4962
4936
|
const isGlobal = args.includes('--global');
|
|
4963
4937
|
const targetPath = isGlobal
|
|
4964
|
-
? path.join(HOME, '
|
|
4938
|
+
? path.join(configDir(HOME), 'providers.json')
|
|
4965
4939
|
: path.join(process.cwd(), '.cx', 'providers.json');
|
|
4966
4940
|
let data = { providers: [] };
|
|
4967
4941
|
try { data = JSON.parse(fs.readFileSync(targetPath, 'utf8')); } catch { /* fresh */ }
|
|
@@ -5486,7 +5460,7 @@ async function cmdTelemetryQuery(args) {
|
|
|
5486
5460
|
println(' errors [--agent=<name>] [--since=24h] error span summary');
|
|
5487
5461
|
println(' trace <traceId> full span tree');
|
|
5488
5462
|
println('');
|
|
5489
|
-
println('For Langfuse/Honeycomb/Grafana Tempo/Datadog, see docs/concepts/observability.md');
|
|
5463
|
+
println('For Langfuse/Honeycomb/Grafana Tempo/Datadog, see docs/guides/concepts/observability.md');
|
|
5490
5464
|
}
|
|
5491
5465
|
|
|
5492
5466
|
async function cmdHook(args) {
|
|
@@ -6553,7 +6527,7 @@ const handlers = new Map([
|
|
|
6553
6527
|
|
|
6554
6528
|
println('');
|
|
6555
6529
|
println(`Scaffolding a new profile draft. Three questions, then a preview.`);
|
|
6556
|
-
println(`Background: docs/concepts/persona-research.md and docs/concepts/profile-lifecycle.md`);
|
|
6530
|
+
println(`Background: docs/guides/concepts/persona-research.md and docs/guides/concepts/profile-lifecycle.md`);
|
|
6557
6531
|
println('');
|
|
6558
6532
|
if (!displayName) displayName = await ask('Display name?', id);
|
|
6559
6533
|
if (seedDepartments.length === 0) {
|
|
@@ -6612,7 +6586,7 @@ const handlers = new Map([
|
|
|
6612
6586
|
println('');
|
|
6613
6587
|
println(`✓ draft created at ${pathMod.relative(process.cwd(), result.dir)}`);
|
|
6614
6588
|
println('');
|
|
6615
|
-
println(`Next steps (in order, per docs/concepts/profile-lifecycle.md):`);
|
|
6589
|
+
println(`Next steps (in order, per docs/guides/concepts/profile-lifecycle.md):`);
|
|
6616
6590
|
println(` 1. Discover (cx-ux-researcher): fill personas/<role>.md from interviews + primary sources.`);
|
|
6617
6591
|
println(` 2. Frame (cx-product-manager): fill departments/<dept>.md charters and intake taxonomy.`);
|
|
6618
6592
|
println(` 3. Architect (cx-architect): reconcile role reuse vs new; populate departments[] in profile.json.`);
|
package/commands/plan/decide.md
CHANGED
|
@@ -11,4 +11,4 @@ ADR format:
|
|
|
11
11
|
- FILES AFFECTED: paths future contributors should inspect
|
|
12
12
|
- FOLLOW-UP: docs, tests, migrations, or risks to track
|
|
13
13
|
|
|
14
|
-
Save to `docs/adr/ADR-{NNN}-{slug}.md` and `.cx/decisions/` if those directories exist.
|
|
14
|
+
Save to `docs/decisions/adr/ADR-{NNN}-{slug}.md` and `.cx/decisions/` if those directories exist.
|
|
@@ -11,4 +11,4 @@ Structure:
|
|
|
11
11
|
- ROLLBACK: how to undo if something goes wrong
|
|
12
12
|
- ESCALATION: who to contact and when
|
|
13
13
|
|
|
14
|
-
Save to `docs/runbooks/{service}-{operation}.md` if the directory exists.
|
|
14
|
+
Save to `docs/operations/runbooks/{service}-{operation}.md` if the directory exists.
|
package/examples/README.md
CHANGED
|
@@ -78,7 +78,7 @@ Required internal coverage:
|
|
|
78
78
|
- Name the expected behavior in repo terms: branch confirmation, approval boundary, routing, verification, blocker surfacing, anti-pattern avoidance.
|
|
79
79
|
- For bad examples, show the failure plainly and explain why it violates the surface contract.
|
|
80
80
|
|
|
81
|
-
See `docs/concepts/prompt-surfaces.md` for the canonical public-vs-internal taxonomy.
|
|
81
|
+
See `docs/guides/concepts/prompt-surfaces.md` for the canonical public-vs-internal taxonomy.
|
|
82
82
|
|
|
83
83
|
## Scope
|
|
84
84
|
|
|
@@ -39,4 +39,4 @@ Edit sources, then re-run the generator. Outputs land in `.tmp/distribution-exam
|
|
|
39
39
|
node bin/construct export examples/distribution/sources/adr.md --to=pdf --figures
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
See [branding.md](../../docs/reference/branding.md) and [diagram-and-demo.md](../../docs/cookbook/diagram-and-demo.md).
|
|
42
|
+
See [branding.md](../../docs/guides/reference/branding.md) and [diagram-and-demo.md](../../docs/guides/cookbook/diagram-and-demo.md).
|
|
@@ -51,7 +51,7 @@ tokens -> dash
|
|
|
51
51
|
|
|
52
52
|
## Decision
|
|
53
53
|
|
|
54
|
-
Adopt `lib/brand-tokens.mjs` as the single source of truth for visual brand primitives. Document naming, voice, tone, and profile rebrand in `docs/reference/branding.md`. Enforce retired-font drift with `scripts/audit/03d-brand.mjs` in the audit ratchet.
|
|
54
|
+
Adopt `lib/brand-tokens.mjs` as the single source of truth for visual brand primitives. Document naming, voice, tone, and profile rebrand in `docs/guides/reference/branding.md`. Enforce retired-font drift with `scripts/audit/03d-brand.mjs` in the audit ratchet.
|
|
55
55
|
|
|
56
56
|
## Rationale
|
|
57
57
|
|
|
@@ -79,6 +79,6 @@ Two-way door for documentation and audit scope; one-way for shipped PDFs already
|
|
|
79
79
|
|
|
80
80
|
## References
|
|
81
81
|
|
|
82
|
-
- `docs/reference/branding.md`
|
|
82
|
+
- `docs/guides/reference/branding.md`
|
|
83
83
|
- `lib/brand-tokens.mjs`
|
|
84
84
|
- https://fonts.google.com/specimen/Space+Grotesk (accessed 2026-06-22)
|
|
@@ -60,6 +60,6 @@ One token set across all distributable formats:
|
|
|
60
60
|
|
|
61
61
|
- Export: `construct export … --to=pptx`
|
|
62
62
|
- Regenerate: `npm run examples:deck`
|
|
63
|
-
- Matrix: [Document I/O reference](/reference/document-io)
|
|
63
|
+
- Matrix: [Document I/O reference](/guides/reference/document-io)
|
|
64
64
|
|
|
65
65
|
Review outputs in `.tmp/distribution-examples/`.
|
|
@@ -157,5 +157,5 @@ https://langchain-ai.github.io/langgraph/ (accessed 2026-06-19).
|
|
|
157
157
|
|
|
158
158
|
- Construct artifact manifest: `specialists/artifact-manifest.json`
|
|
159
159
|
- PRD workflow skill: `skills/docs/prd-workflow.md`
|
|
160
|
-
- Publish cookbook: `docs/cookbook/diagram-and-demo.md`
|
|
160
|
+
- Publish cookbook: `docs/guides/cookbook/diagram-and-demo.md`
|
|
161
161
|
- Golden D2 sources: `tests/fixtures/publish/diagrams/`
|
|
@@ -32,7 +32,7 @@ Searched arXiv and vendor docs (2024–2026), reviewed three internal ADRs on in
|
|
|
32
32
|
|---|---|---|---|---|---|---|---|
|
|
33
33
|
| Multi-agent survey | secondary | B | 3 | 2023-08-01 | https://arxiv.org/abs/2308.08155 | yes | Patterns for specialist routing |
|
|
34
34
|
| LangGraph overview | primary | A | 4 | 2025-01-15 | https://langchain-ai.github.io/langgraph/ | yes | Stateful agent graphs |
|
|
35
|
-
| Construct architecture | primary | A | 5 | 2026-06-01 | docs/concepts/architecture.mdx | yes | Internal contract |
|
|
35
|
+
| Construct architecture | primary | A | 5 | 2026-06-01 | docs/guides/concepts/architecture.mdx | yes | Internal contract |
|
|
36
36
|
|
|
37
37
|
## Findings
|
|
38
38
|
|
|
@@ -77,4 +77,4 @@ Ship distribution examples (`npm run examples:distribution`) so evaluators can c
|
|
|
77
77
|
## References
|
|
78
78
|
|
|
79
79
|
- https://arxiv.org/abs/2308.08155 (accessed 2026-06-22)
|
|
80
|
-
- `docs/cookbook/diagram-and-demo.md`
|
|
80
|
+
- `docs/guides/cookbook/diagram-and-demo.md`
|
|
@@ -99,5 +99,5 @@ Preserve `construct tools detect --json` output and the failing pandoc/typst std
|
|
|
99
99
|
|
|
100
100
|
## References
|
|
101
101
|
|
|
102
|
-
- `docs/reference/document-io.md`
|
|
103
|
-
- `docs/cookbook/diagram-and-demo.md`
|
|
102
|
+
- `docs/guides/reference/document-io.md`
|
|
103
|
+
- `docs/guides/cookbook/diagram-and-demo.md`
|
|
@@ -26,7 +26,7 @@ Host-native agents optimize for single-session chat. Enterprise platform teams n
|
|
|
26
26
|
## Goals
|
|
27
27
|
|
|
28
28
|
1. **Prove value in five minutes** — `npm run examples:distribution` produces a browsable gallery with figures.
|
|
29
|
-
2. **One brand contract** — `docs/reference/branding.md` + `03d-brand` audit ratchet.
|
|
29
|
+
2. **One brand contract** — `docs/guides/reference/branding.md` + `03d-brand` audit ratchet.
|
|
30
30
|
3. **Profile-aware language** — intake queue labels follow active profile rebrand in CLI, session prelude, and dashboard API.
|
|
31
31
|
|
|
32
32
|
## Non-goals
|
|
@@ -71,7 +71,7 @@ export function buildConstructIntegrationBody({ hasBeadsBlock = false, variant =
|
|
|
71
71
|
'- **Durable state** lives in `.cx/` (context, knowledge, intake, traces) and Beads.',
|
|
72
72
|
' If services are down, resume from `plan.md`, `.cx/context.md`, the latest',
|
|
73
73
|
' `.cx/handoffs/` file, Beads, and git.',
|
|
74
|
-
'- **Signals**: drop a file into
|
|
74
|
+
'- **Signals**: drop a file into `inbox/`; `construct intake` classifies and routes it.',
|
|
75
75
|
'- **Specialists** (architect, reviewer, security, …) are dispatched by Construct — you',
|
|
76
76
|
' do not call them directly.',
|
|
77
77
|
tracker,
|
|
@@ -41,12 +41,16 @@ function inferRfcSubtype(rel, base) {
|
|
|
41
41
|
return 'rfc';
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Match both this repo's bucketed layout (docs/specs/prd, docs/decisions/adr, …)
|
|
45
|
+
// and the init-lane layout that `construct init` scaffolds downstream (docs/prd,
|
|
46
|
+
// docs/adr, …), so artifact-type inference works in either project shape.
|
|
47
|
+
|
|
44
48
|
const PREFIX_RULES = [
|
|
45
|
-
[/^docs\/prd\//, inferPrdSubtype],
|
|
46
|
-
[/^docs\/adr\//, () => 'adr'],
|
|
47
|
-
[/^docs\/rfc\//, inferRfcSubtype],
|
|
48
|
-
[/^docs\/research\//, () => 'research-brief'],
|
|
49
|
-
[/^docs\/runbooks?\//, () => 'runbook'],
|
|
49
|
+
[/^docs\/(?:specs\/)?prd\//, inferPrdSubtype],
|
|
50
|
+
[/^docs\/(?:decisions\/)?adr\//, () => 'adr'],
|
|
51
|
+
[/^docs\/(?:decisions\/)?rfc\//, inferRfcSubtype],
|
|
52
|
+
[/^docs\/(?:notes\/)?research\//, () => 'research-brief'],
|
|
53
|
+
[/^docs\/(?:operations\/)?runbooks?\//, () => 'runbook'],
|
|
50
54
|
[/^\.cx\/research\//, () => 'research-brief'],
|
|
51
55
|
];
|
|
52
56
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Merges live registry, contracts, skills, prompts, and audit-enrichments into
|
|
5
5
|
* one machine-readable matrix. Powers `construct audit specialists` and the
|
|
6
|
-
* human-readable docs/concepts/specialist-skill-audit.md companion.
|
|
6
|
+
* human-readable docs/guides/concepts/specialist-skill-audit.md companion.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import fs from 'node:fs';
|
package/lib/audit-trail.mjs
CHANGED
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
* Corrupted links cannot be re-derived, so a `chain_reset` boundary line seals
|
|
17
17
|
* the damaged prefix as immutable legacy and re-bases the live chain;
|
|
18
18
|
* verifyChain validates only from the last boundary forward. See
|
|
19
|
-
* docs/concepts/observability.md.
|
|
19
|
+
* docs/guides/concepts/observability.md.
|
|
20
20
|
*/
|
|
21
21
|
import { readFileSync, existsSync, statSync, mkdirSync } from 'node:fs';
|
|
22
22
|
import { createHash } from 'node:crypto';
|
|
23
23
|
import { join, dirname } from 'node:path';
|
|
24
|
-
import { homedir } from 'node:os';
|
|
25
24
|
|
|
26
25
|
import { appendBounded, readLastLineAcrossSegments } from './logging/rotate.mjs';
|
|
27
26
|
import { withFileLockSync } from './storage/file-lock.mjs';
|
|
27
|
+
import { doctorRoot } from './config/xdg.mjs';
|
|
28
28
|
|
|
29
|
-
const AUDIT_FILE = join(
|
|
29
|
+
const AUDIT_FILE = join(doctorRoot(), 'audit-trail.jsonl');
|
|
30
30
|
|
|
31
31
|
function sha256(input) {
|
|
32
32
|
return createHash('sha256').update(input).digest('hex');
|
package/lib/auto-docs.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* buildSite() is retained for compatibility; the public site now renders docs/
|
|
13
13
|
* directly through Next.js (apps/docs/) and buildFumadocsReference() emits
|
|
14
|
-
* generated reference Markdown into docs/reference/.
|
|
14
|
+
* generated reference Markdown into docs/guides/reference/.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import fs from 'node:fs';
|
|
@@ -78,7 +78,7 @@ function buildCoreDocsContract() {
|
|
|
78
78
|
'| `.cx/context.md` | Human-readable resumable project context | Active work, decisions, architecture assumptions, or open questions change |',
|
|
79
79
|
'| `.cx/context.json` | Machine-readable resumable context | Context state needs to stay in sync with `.cx/context.md` |',
|
|
80
80
|
'| `docs/README.md` | Docs index and maintenance contract | Core docs set or maintenance expectations change |',
|
|
81
|
-
'| `docs/concepts/architecture.mdx` | Canonical architecture and invariants | Runtime shape, contracts, boundaries, or major dependencies change |',
|
|
81
|
+
'| `docs/guides/concepts/architecture.mdx` | Canonical architecture and invariants | Runtime shape, contracts, boundaries, or major dependencies change |',
|
|
82
82
|
'',
|
|
83
83
|
'`plan.md` is a local working document. `construct init` creates it for the active session, but it is gitignored and not committed; durable work belongs in the tracker (Beads or external).',
|
|
84
84
|
'',
|
|
@@ -191,7 +191,7 @@ function buildAgentsTable(rootDir) {
|
|
|
191
191
|
// --- public API ---
|
|
192
192
|
|
|
193
193
|
/**
|
|
194
|
-
* Regenerate all AUTO regions in README.md, docs/README.md, and docs/concepts/architecture.mdx.
|
|
194
|
+
* Regenerate all AUTO regions in README.md, docs/README.md, and docs/guides/concepts/architecture.mdx.
|
|
195
195
|
* Returns { changed: string[], checked: boolean }.
|
|
196
196
|
* With check:true writes nothing and sets changed to files that would differ.
|
|
197
197
|
*/
|
|
@@ -209,7 +209,7 @@ export async function regenerateDocs({ rootDir, check = false } = {}) {
|
|
|
209
209
|
},
|
|
210
210
|
},
|
|
211
211
|
{
|
|
212
|
-
file: path.join(rootDir, 'docs', 'concepts', 'architecture.mdx'),
|
|
212
|
+
file: path.join(rootDir, 'docs', 'guides', 'concepts', 'architecture.mdx'),
|
|
213
213
|
regions: {
|
|
214
214
|
agents: buildAgentsTable(rootDir),
|
|
215
215
|
},
|
|
@@ -268,7 +268,7 @@ export function checkDocsCoverage({ rootDir } = {}) {
|
|
|
268
268
|
// Build a combined corpus: docs/README.md + every linked guide file
|
|
269
269
|
let corpus = docsReadme;
|
|
270
270
|
for (const target of linkTargets) {
|
|
271
|
-
if (!target.startsWith('./
|
|
271
|
+
if (!target.startsWith('./guides/cookbook/')) continue;
|
|
272
272
|
const filePath = path.join(rootDir, 'docs', target.replace(/^\.\//, ''));
|
|
273
273
|
const content = readFile(filePath);
|
|
274
274
|
if (content) corpus += '\n' + content;
|
|
@@ -423,7 +423,7 @@ function renderAgentsPage(rootDir) {
|
|
|
423
423
|
}
|
|
424
424
|
|
|
425
425
|
/**
|
|
426
|
-
* Emit MDX reference pages into docs/reference/ for the Next.js docs site
|
|
426
|
+
* Emit MDX reference pages into docs/guides/reference/ for the Next.js docs site
|
|
427
427
|
* in apps/docs/. Mirrors the data sources behind AUTO regions and buildSite()
|
|
428
428
|
* but writes Markdown files the docs app can pull in directly (no MkDocs
|
|
429
429
|
* intermediate). Name kept for backwards compatibility with callers that
|
|
@@ -432,7 +432,7 @@ function renderAgentsPage(rootDir) {
|
|
|
432
432
|
*/
|
|
433
433
|
export function buildFumadocsReference({ rootDir } = {}) {
|
|
434
434
|
rootDir = rootDir ?? process.cwd();
|
|
435
|
-
const refDir = path.join(rootDir, 'docs', 'reference');
|
|
435
|
+
const refDir = path.join(rootDir, 'docs', 'guides', 'reference');
|
|
436
436
|
const cliDir = path.join(refDir, 'cli');
|
|
437
437
|
fs.mkdirSync(cliDir, { recursive: true });
|
|
438
438
|
|
|
@@ -468,7 +468,7 @@ export function buildFumadocsReference({ rootDir } = {}) {
|
|
|
468
468
|
const slug = slugify(cat);
|
|
469
469
|
const commands = CLI_COMMANDS_BY_CATEGORY[cat] ?? [];
|
|
470
470
|
if (!commands.length) return null;
|
|
471
|
-
return `- [${cat}](/reference/cli/${slug}) — ${commands.length} command${commands.length === 1 ? '' : 's'}`;
|
|
471
|
+
return `- [${cat}](/guides/reference/cli/${slug}) — ${commands.length} command${commands.length === 1 ? '' : 's'}`;
|
|
472
472
|
}).filter(Boolean),
|
|
473
473
|
'',
|
|
474
474
|
].join('\n');
|
|
@@ -499,7 +499,7 @@ export function buildFumadocsReference({ rootDir } = {}) {
|
|
|
499
499
|
|
|
500
500
|
export function checkFumadocsReferenceDrift({ rootDir } = {}) {
|
|
501
501
|
rootDir = rootDir ?? process.cwd();
|
|
502
|
-
const refDir = path.join(rootDir, 'docs', 'reference');
|
|
502
|
+
const refDir = path.join(rootDir, 'docs', 'guides', 'reference');
|
|
503
503
|
const cliDir = path.join(refDir, 'cli');
|
|
504
504
|
const drift = [];
|
|
505
505
|
|
|
@@ -534,7 +534,7 @@ export function checkFumadocsReferenceDrift({ rootDir } = {}) {
|
|
|
534
534
|
const slug = slugify(cat);
|
|
535
535
|
const commands = CLI_COMMANDS_BY_CATEGORY[cat] ?? [];
|
|
536
536
|
if (!commands.length) return null;
|
|
537
|
-
return `- [${cat}](/reference/cli/${slug}) — ${commands.length} command${commands.length === 1 ? '' : 's'}`;
|
|
537
|
+
return `- [${cat}](/guides/reference/cli/${slug}) — ${commands.length} command${commands.length === 1 ? '' : 's'}`;
|
|
538
538
|
}).filter(Boolean),
|
|
539
539
|
'',
|
|
540
540
|
].join('\n');
|