@geraldmaron/construct 1.2.1 → 1.2.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.
- package/README.md +6 -6
- package/apps/chat/engine/ai-sdk-agent.mjs +183 -0
- package/apps/chat/engine/loop-driver.mjs +211 -0
- package/apps/chat/engine/models.mjs +122 -0
- package/apps/chat/engine/provider-adapters.mjs +171 -0
- package/apps/chat/engine/tools/permission.mjs +54 -0
- package/apps/chat/engine/tools/primitives.mjs +180 -0
- package/apps/chat/engine/tools/registry.mjs +122 -0
- package/apps/chat/engine/turn-controls.mjs +70 -0
- package/bin/construct +93 -79
- 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 +4 -6
- 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/legacy-config-migration.mjs +59 -0
- 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-env.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 +21 -11
- 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 +4 -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/lib/hooks/test-watch.mjs
CHANGED
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
18
|
-
import { homedir } from 'node:os';
|
|
19
18
|
import { join } from 'node:path';
|
|
20
19
|
import { logHookFailure } from './_lib/log.mjs';
|
|
21
20
|
import { emitRoleEvent } from '../roles/hook-emit.mjs';
|
|
21
|
+
import { doctorRoot } from '../config/xdg.mjs';
|
|
22
22
|
|
|
23
23
|
let input = {};
|
|
24
24
|
try { input = JSON.parse(readFileSync(0, 'utf8')); }
|
|
@@ -44,7 +44,7 @@ emitRoleEvent({
|
|
|
44
44
|
context: { command, exitCode, failedNames },
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
const flakeTrackerPath = join(
|
|
47
|
+
const flakeTrackerPath = join(doctorRoot(), 'test-watch-state.json');
|
|
48
48
|
try {
|
|
49
49
|
const state = existsSync(flakeTrackerPath) ? JSON.parse(readFileSync(flakeTrackerPath, 'utf8')) : {};
|
|
50
50
|
const now = Date.now();
|
package/lib/host-disposition.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* lib/host-disposition.mjs — single source of truth for the disposition of
|
|
3
3
|
* every artifact Construct creates in a host project (ADR-0027 §1).
|
|
4
4
|
*
|
|
5
|
-
* Scope model (see docs/concepts/project-scopes.md):
|
|
5
|
+
* Scope model (see docs/guides/concepts/project-scopes.md):
|
|
6
6
|
* templates/, docs/ — package/repo durable sources (committed)
|
|
7
7
|
* .construct/ — per-project launcher (ignored)
|
|
8
8
|
* .cx/ — per-project runtime: intake, oracle, observations, demos (ignored)
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import { readFileSync, existsSync } from 'node:fs';
|
|
25
25
|
import { extname, join } from 'node:path';
|
|
26
26
|
import { homedir } from 'node:os';
|
|
27
|
+
import { configDir } from '../config/xdg.mjs';
|
|
27
28
|
|
|
28
29
|
import {
|
|
29
30
|
AUDIO_VIDEO_EXTS,
|
|
@@ -75,7 +76,7 @@ function isAnthropic(provider, model) {
|
|
|
75
76
|
function resolveKey(varName, env, allowAmbient) {
|
|
76
77
|
if (env[varName] && typeof env[varName] === 'string' && env[varName].length > 0) return env[varName];
|
|
77
78
|
if (!allowAmbient) return null;
|
|
78
|
-
for (const file of [join(
|
|
79
|
+
for (const file of [join(configDir(), 'config.env'), join(homedir(), '.env')]) {
|
|
79
80
|
try {
|
|
80
81
|
if (!existsSync(file)) continue;
|
|
81
82
|
const m = readFileSync(file, 'utf8').match(new RegExp(`^${varName}=["']?(.+?)["']?$`, 'm'));
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Heuristics intentionally err on the side of "skip" — a directory is treated
|
|
14
14
|
* as a real lane only when it carries at least one markdown file. An empty
|
|
15
|
-
* docs/meetings/ that an earlier init scaffolded does NOT register; that lets
|
|
15
|
+
* docs/notes/meetings/ that an earlier init scaffolded does NOT register; that lets
|
|
16
16
|
* re-running init be a no-op rather than a contradictory "skip what we just
|
|
17
17
|
* created" message.
|
|
18
18
|
*
|
|
19
19
|
* Issue #97 motivates this: `construct init` on an existing project with
|
|
20
20
|
* `internal/meetings/`, a custom `ingest` script, and root-level `templates/`
|
|
21
|
-
* created `docs/meetings/`,
|
|
21
|
+
* created `docs/notes/meetings/`, `inbox/`, and per-lane `templates/` folders
|
|
22
22
|
* that conflicted with the existing workflow.
|
|
23
23
|
*/
|
|
24
24
|
|
package/lib/init/doc-lanes.mjs
CHANGED
|
@@ -32,12 +32,6 @@ export const DOC_LANES = {
|
|
|
32
32
|
description: 'User-facing release notes and version history entries.',
|
|
33
33
|
templates: ['changelog-entry.md'],
|
|
34
34
|
},
|
|
35
|
-
intake: {
|
|
36
|
-
title: 'Intake',
|
|
37
|
-
dir: 'intake',
|
|
38
|
-
description: 'Intake batch records that explain what arrived, why it matters, and how it should be ingested.',
|
|
39
|
-
templates: ['__intake-template__'],
|
|
40
|
-
},
|
|
41
35
|
memos: {
|
|
42
36
|
title: 'Memos',
|
|
43
37
|
dir: 'memos',
|
|
@@ -88,11 +82,11 @@ export const DOC_LANES = {
|
|
|
88
82
|
},
|
|
89
83
|
};
|
|
90
84
|
|
|
91
|
-
export const LANE_ORDER = ['adrs', 'briefs', 'changelogs', '
|
|
85
|
+
export const LANE_ORDER = ['adrs', 'briefs', 'changelogs', 'memos', 'meetings', 'notes', 'onboarding', 'postmortems', 'prds', 'rfcs', 'runbooks'];
|
|
92
86
|
|
|
93
87
|
export const DOC_PRESETS = {
|
|
94
|
-
lean: ['adrs', '
|
|
95
|
-
product: ['adrs', '
|
|
88
|
+
lean: ['adrs', 'memos', 'meetings', 'notes', 'prds'],
|
|
89
|
+
product: ['adrs', 'memos', 'meetings', 'notes', 'prds', 'rfcs'],
|
|
96
90
|
full: LANE_ORDER,
|
|
97
91
|
};
|
|
98
92
|
|
|
@@ -107,7 +101,6 @@ export const LANE_ALIASES = {
|
|
|
107
101
|
changelogs: 'changelogs',
|
|
108
102
|
releases: 'changelogs',
|
|
109
103
|
release: 'changelogs',
|
|
110
|
-
intake: 'intake',
|
|
111
104
|
memo: 'memos',
|
|
112
105
|
memos: 'memos',
|
|
113
106
|
meeting: 'meetings',
|
package/lib/init-docs.mjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* lib/init-docs.mjs — stand up a documentation system for a project.
|
|
4
4
|
*
|
|
5
5
|
* Intentionally separate from `construct init`. Creates the docs surface only:
|
|
6
|
-
* docs/README.md plus selected lane directories such as docs/adr/, docs/intake/,
|
|
7
|
-
* docs/memos/, docs/notes/, docs/prds/, and docs/rfcs/ with starter templates
|
|
6
|
+
* docs/README.md plus selected lane directories such as docs/decisions/adr/, docs/intake/,
|
|
7
|
+
* docs/notes/memos/, docs/notes/, templates/docs/prds/, and templates/docs/rfcs/ with starter templates
|
|
8
8
|
* copied into per-lane templates/ directories from Construct's template library.
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
@@ -290,15 +290,6 @@ function buildLaneReadme(laneKey) {
|
|
|
290
290
|
const filename = index === 0 ? "_template.md" : templateName.replace(/\.md$/, ".template.md");
|
|
291
291
|
return `- [${filename}](./templates/${filename})`;
|
|
292
292
|
});
|
|
293
|
-
const usageSection = laneKey === "intake"
|
|
294
|
-
? `
|
|
295
|
-
## Intake flow
|
|
296
|
-
|
|
297
|
-
- Drop source files into either [\`.cx/inbox/\`](../../.cx/inbox/) or this lane's directory when you want Construct to ingest them.
|
|
298
|
-
- Run \`construct ingest ./docs/intake --sync\` or \`construct ingest ./.cx/inbox --sync\` to convert supported files into retrieval-ready markdown, or let the embed daemon watch those drop zones automatically.
|
|
299
|
-
- Durable ingested knowledge lands under \`.cx/knowledge/internal/\` by default, which is where Construct's learning and search paths already operate.
|
|
300
|
-
`
|
|
301
|
-
: "";
|
|
302
293
|
|
|
303
294
|
return `<!--
|
|
304
295
|
docs/${dirName}/README.md — lane guide for ${title}.
|
|
@@ -314,7 +305,6 @@ ${description}
|
|
|
314
305
|
## Starter templates
|
|
315
306
|
|
|
316
307
|
${templateLines.join("\n")}
|
|
317
|
-
${usageSection}
|
|
318
308
|
`;
|
|
319
309
|
}
|
|
320
310
|
|
|
@@ -432,39 +422,6 @@ Promote durable decisions into ADRs, memos, or PRDs when needed.
|
|
|
432
422
|
`;
|
|
433
423
|
}
|
|
434
424
|
|
|
435
|
-
function buildIntakeTemplate() {
|
|
436
|
-
return `<!--
|
|
437
|
-
docs/intake/templates/_template.md — starter template for logging an intake batch.
|
|
438
|
-
|
|
439
|
-
Use this when you want a durable record of what was dropped into intake and why.
|
|
440
|
-
Raw source files belong in .cx/inbox/ until they are ingested.
|
|
441
|
-
-->
|
|
442
|
-
|
|
443
|
-
# Intake Batch: {title}
|
|
444
|
-
|
|
445
|
-
- **Date**: {YYYY-MM-DD}
|
|
446
|
-
- **Owner**: {name}
|
|
447
|
-
- **Source**: {vendor, teammate, export, upload}
|
|
448
|
-
|
|
449
|
-
## What arrived
|
|
450
|
-
|
|
451
|
-
<!-- Files, folders, or links dropped into .cx/inbox/. -->
|
|
452
|
-
|
|
453
|
-
## Why it matters
|
|
454
|
-
|
|
455
|
-
<!-- Why Construct should ingest this material. -->
|
|
456
|
-
|
|
457
|
-
## Ingest plan
|
|
458
|
-
|
|
459
|
-
- Run: \`construct ingest ./.cx/inbox --sync\`
|
|
460
|
-
- Target: \`.cx/knowledge/internal/\` unless a different knowledge target is more appropriate
|
|
461
|
-
|
|
462
|
-
## Notes
|
|
463
|
-
|
|
464
|
-
<!-- Caveats, access concerns, or cleanup notes. -->
|
|
465
|
-
`;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
425
|
function titleCase(value) {
|
|
469
426
|
return value
|
|
470
427
|
.split(/[-_\s]+/)
|
|
@@ -532,7 +489,6 @@ function copyLaneTemplates(laneKey) {
|
|
|
532
489
|
const content =
|
|
533
490
|
templateName === "__notes-template__" ? buildNotesTemplate()
|
|
534
491
|
: templateName === '__meeting-notes-template__' ? buildMeetingNotesTemplate()
|
|
535
|
-
: templateName === "__intake-template__" ? buildIntakeTemplate()
|
|
536
492
|
: fs.readFileSync(path.join(TEMPLATE_DIR, templateName), "utf8");
|
|
537
493
|
writeIfMissing(path.join(laneRoot, "templates", outputName), content);
|
|
538
494
|
}
|
|
@@ -632,14 +588,16 @@ async function main() {
|
|
|
632
588
|
if (withArchitecture) {
|
|
633
589
|
writeIfMissing(path.join(docsDir, "architecture.md"), buildArchitectureDoc(projectName, allScaffoldedLaneKeys));
|
|
634
590
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
591
|
+
const inboxDecision = shouldSkipProjectInbox(detection, { force: forceScaffold });
|
|
592
|
+
if (inboxDecision.skip) {
|
|
593
|
+
process.stdout.write(`[init:docs] skipping inbox/ — ${inboxDecision.reason}. Run with --force to scaffold anyway.\n`);
|
|
594
|
+
skipped.push('inbox/ (deferred to existing intake)');
|
|
595
|
+
} else {
|
|
596
|
+
// Single canonical drop zone (ADR-0045 §C): the project-root inbox/, with
|
|
597
|
+
// a gitignored .staging/ for atomic rename-in handoff. Independent of any
|
|
598
|
+
// docs lane.
|
|
599
|
+
|
|
600
|
+
fs.mkdirSync(path.join(target, 'inbox', '.staging'), { recursive: true });
|
|
643
601
|
}
|
|
644
602
|
|
|
645
603
|
for (const { laneKey, reason } of deferredLanes) {
|
package/lib/init-unified.mjs
CHANGED
|
@@ -175,10 +175,7 @@ function detectProjectType(targetPath) {
|
|
|
175
175
|
function suggestLanes(targetPath, projectType) {
|
|
176
176
|
const suggestions = [];
|
|
177
177
|
const files = fs.readdirSync(targetPath);
|
|
178
|
-
|
|
179
|
-
// Always suggest intake for collecting feedback
|
|
180
|
-
suggestions.push({ lane: 'intake', reason: 'Collect incoming requests and feedback', recommended: true });
|
|
181
|
-
|
|
178
|
+
|
|
182
179
|
// ADRs for any non-trivial project
|
|
183
180
|
if (projectType.type !== 'docs') {
|
|
184
181
|
suggestions.push({ lane: 'adrs', reason: 'Track architectural decisions', recommended: true });
|
|
@@ -365,33 +362,6 @@ Attendees: {{attendees}}
|
|
|
365
362
|
- [ ] @owner: Task description (due: {{date}})`;
|
|
366
363
|
}
|
|
367
364
|
|
|
368
|
-
function buildIntakeTemplate() {
|
|
369
|
-
return `# Intake: {{source}}
|
|
370
|
-
|
|
371
|
-
Received: {{date}}
|
|
372
|
-
From: {{from}}
|
|
373
|
-
Priority: {{priority}}
|
|
374
|
-
|
|
375
|
-
## What arrived
|
|
376
|
-
|
|
377
|
-
Brief description of what was received.
|
|
378
|
-
|
|
379
|
-
## Why it matters
|
|
380
|
-
|
|
381
|
-
Why this intake item is important to process.
|
|
382
|
-
|
|
383
|
-
## How it should be ingested
|
|
384
|
-
|
|
385
|
-
Processing instructions:
|
|
386
|
-
- [ ] Step 1
|
|
387
|
-
- [ ] Step 2
|
|
388
|
-
- [ ] Step 3
|
|
389
|
-
|
|
390
|
-
## Notes
|
|
391
|
-
|
|
392
|
-
<!-- Caveats, access concerns, or cleanup notes. -->`;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
365
|
function writeIfMissing(filePath, content) {
|
|
396
366
|
if (fs.existsSync(filePath)) {
|
|
397
367
|
skipped.push(path.relative(target, filePath));
|
|
@@ -418,8 +388,6 @@ function copyLaneTemplates(laneKey) {
|
|
|
418
388
|
content = buildNotesTemplate();
|
|
419
389
|
} else if (templateName === '__meeting-notes-template__') {
|
|
420
390
|
content = buildMeetingNotesTemplate();
|
|
421
|
-
} else if (templateName === "__intake-template__") {
|
|
422
|
-
content = buildIntakeTemplate();
|
|
423
391
|
} else {
|
|
424
392
|
const templatePath = path.join(TEMPLATE_DIR, templateName);
|
|
425
393
|
if (fs.existsSync(templatePath)) {
|
|
@@ -543,8 +511,8 @@ function discoverProjectDirs(targetPath) {
|
|
|
543
511
|
// Extra inbox-style directories the user can opt into. These are NOT
|
|
544
512
|
// source-code, doc, or test directories: they're places where unsorted
|
|
545
513
|
// signals tend to land (downloads, scratch notes, exported research). The
|
|
546
|
-
//
|
|
547
|
-
//
|
|
514
|
+
// canonical `inbox/` at the project root is always watched and not listed
|
|
515
|
+
// here.
|
|
548
516
|
//
|
|
549
517
|
// Historically this list included `src/`, `docs/`, `tests/`, etc., and
|
|
550
518
|
// `init --yes` auto-enabled every one that existed in the project. That
|
|
@@ -555,10 +523,9 @@ function discoverProjectDirs(targetPath) {
|
|
|
555
523
|
const INTAKE_DIR_PRESETS = [];
|
|
556
524
|
|
|
557
525
|
async function askIntakeCollection(targetPath, skipInteractive) {
|
|
558
|
-
// Non-interactive default: empty parentDirs. The
|
|
559
|
-
//
|
|
560
|
-
//
|
|
561
|
-
// explicitly via `construct intake config set --add-dir=<path>`.
|
|
526
|
+
// Non-interactive default: empty parentDirs. The canonical `inbox/` zone
|
|
527
|
+
// handles every well-defined signal path. Users who want to watch additional
|
|
528
|
+
// directories opt in explicitly via `construct intake config set --add-dir=<path>`.
|
|
562
529
|
if (skipInteractive) {
|
|
563
530
|
return null;
|
|
564
531
|
}
|
|
@@ -583,7 +550,7 @@ async function askIntakeCollection(targetPath, skipInteractive) {
|
|
|
583
550
|
console.log(' INTAKE COLLECTION');
|
|
584
551
|
console.log('═══════════════════════════════════════════════════════════');
|
|
585
552
|
console.log('');
|
|
586
|
-
console.log('Intake watches
|
|
553
|
+
console.log('Intake watches `inbox/` at the project root.');
|
|
587
554
|
console.log('Add extra directories ONLY if signals (customer notes, exports,');
|
|
588
555
|
console.log('PDFs, etc.) regularly land there. Do not select code or finished');
|
|
589
556
|
console.log('artifact directories like src/, docs/, or tests/.');
|
|
@@ -599,7 +566,7 @@ async function askIntakeCollection(targetPath, skipInteractive) {
|
|
|
599
566
|
if (selected.length === 0) {
|
|
600
567
|
console.log('');
|
|
601
568
|
console.log('No extra directories selected. The inbox watcher will scan');
|
|
602
|
-
console.log('
|
|
569
|
+
console.log('`inbox/` only. Add more later with:');
|
|
603
570
|
console.log(' construct intake config set --add-dir=<path>');
|
|
604
571
|
console.log('');
|
|
605
572
|
return null;
|
|
@@ -866,37 +833,35 @@ async function main() {
|
|
|
866
833
|
|
|
867
834
|
|
|
868
835
|
// Detect existing project content once; the result feeds three decisions
|
|
869
|
-
// below: skip
|
|
870
|
-
// already covered elsewhere, and skip per-lane templates/ when
|
|
871
|
-
// templates/ already has them. --force bypasses every check.
|
|
836
|
+
// below: skip the inbox/ drop zone on custom intake, skip lane scaffolding
|
|
837
|
+
// for lanes already covered elsewhere, and skip per-lane templates/ when
|
|
838
|
+
// root templates/ already has them. --force bypasses every check.
|
|
872
839
|
|
|
873
840
|
const { detectExistingContent, shouldSkipProjectInbox, shouldScaffoldLane, formatDeferralSummary } =
|
|
874
841
|
await import('./init/detect-existing-structure.mjs');
|
|
875
842
|
const detection = detectExistingContent(target);
|
|
876
843
|
const inboxDecision = shouldSkipProjectInbox(detection, { force: forceScaffold });
|
|
877
844
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
fs.mkdirSync(path.join(target, ".cx", "inbox"), { recursive: true });
|
|
886
|
-
created.push(".cx/inbox/");
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
// Universal project-root inbox/ drop zone. Raw drops stay out of source via
|
|
890
|
-
// host-disposition IGNORED_PATTERNS (ADR-0027 §1). Skipped when custom intake
|
|
891
|
-
// pipeline already owns the project root.
|
|
845
|
+
// Single canonical drop zone (ADR-0045 §C): the visible project-root `inbox/`.
|
|
846
|
+
// Raw drops stay out of source via host-disposition IGNORED_PATTERNS
|
|
847
|
+
// (ADR-0027 §1). Machine/runtime intake state lives under the gitignored
|
|
848
|
+
// `.cx/intake/` tree, created lazily by the queue on first use — init no
|
|
849
|
+
// longer scaffolds the deprecated `.cx/inbox/` drop zone. Skipped when a
|
|
850
|
+
// custom intake pipeline already owns the project root.
|
|
892
851
|
|
|
893
852
|
if (!inboxDecision.skip) {
|
|
894
|
-
const
|
|
895
|
-
if (!fs.existsSync(
|
|
896
|
-
fs.mkdirSync(
|
|
853
|
+
const rootInbox = path.join(target, 'inbox');
|
|
854
|
+
if (!fs.existsSync(rootInbox)) {
|
|
855
|
+
fs.mkdirSync(rootInbox, { recursive: true });
|
|
897
856
|
created.push('inbox/');
|
|
898
857
|
}
|
|
899
858
|
|
|
859
|
+
// Writers assemble files under `inbox/.staging/` and atomically rename them
|
|
860
|
+
// into `inbox/`; the gitignored staging dir keeps a half-written drop
|
|
861
|
+
// invisible to the watcher until it lands.
|
|
862
|
+
|
|
863
|
+
fs.mkdirSync(path.join(rootInbox, '.staging'), { recursive: true });
|
|
864
|
+
|
|
900
865
|
if (intakeCap?.dedup === 'sha256') {
|
|
901
866
|
const { saveManifest, loadManifest, MANIFEST_REL_PATH } = await import('./intake/manifest.mjs');
|
|
902
867
|
const manifestExists = fs.existsSync(path.join(target, MANIFEST_REL_PATH));
|
|
@@ -906,11 +871,10 @@ async function main() {
|
|
|
906
871
|
}
|
|
907
872
|
}
|
|
908
873
|
} else {
|
|
874
|
+
console.log(`[init:intake] skipping inbox/ — ${inboxDecision.reason}. Run with --force to scaffold anyway.`);
|
|
909
875
|
skipped.push('inbox/ (deferred to existing intake)');
|
|
910
876
|
}
|
|
911
877
|
|
|
912
|
-
// Legacy archetype block removed — root inbox/ is universal (construct-1arm).
|
|
913
|
-
|
|
914
878
|
// Gitignore every Construct-generated artifact whose disposition is `ignored`
|
|
915
879
|
// (ADR-0027 §1): the six adapter dirs, the `.construct/` launcher, `.cx/`
|
|
916
880
|
// runtime state, and the generated config files all carry machine-specific
|
|
@@ -1075,28 +1039,18 @@ async function main() {
|
|
|
1075
1039
|
console.warn(`⚠️ Could not inject agent-instruction block: ${err.message}`);
|
|
1076
1040
|
}
|
|
1077
1041
|
|
|
1078
|
-
// Intake
|
|
1079
|
-
//
|
|
1080
|
-
//
|
|
1081
|
-
//
|
|
1042
|
+
// Intake policy — write the single-zone model (ADR-0045 §C) so the user can
|
|
1043
|
+
// inspect what's watched and edit it directly. The canonical `inbox/` at the
|
|
1044
|
+
// project root is the only drop zone; empty parentDirs is correct — extra
|
|
1045
|
+
// dirs are opt-in.
|
|
1082
1046
|
|
|
1083
1047
|
const intakeConfig = (await askIntakeCollection(target, skipInteractive)) ?? { parentDirs: [], maxDepth: 4 };
|
|
1084
1048
|
|
|
1085
|
-
if (inboxDecision.skip) intakeConfig.includeProjectInbox = false;
|
|
1086
|
-
|
|
1087
|
-
intakeConfig.includeRootInbox = !inboxDecision.skip;
|
|
1088
|
-
intakeConfig.includeArchetypeInbox = intakeConfig.includeRootInbox;
|
|
1089
|
-
|
|
1090
1049
|
const { saveIntakePolicy } = await import('./config/intake-policy.mjs');
|
|
1091
1050
|
try {
|
|
1092
1051
|
saveIntakePolicy(target, {
|
|
1093
1052
|
maxDepth: intakeConfig.maxDepth,
|
|
1094
1053
|
additionalDirs: intakeConfig.parentDirs ?? [],
|
|
1095
|
-
zones: {
|
|
1096
|
-
rootInbox: intakeConfig.includeRootInbox !== false,
|
|
1097
|
-
projectInbox: intakeConfig.includeProjectInbox !== false,
|
|
1098
|
-
docsIntake: intakeConfig.includeDocsIntake !== false,
|
|
1099
|
-
},
|
|
1100
1054
|
});
|
|
1101
1055
|
created.push('construct.config.json (intakePolicy)');
|
|
1102
1056
|
} catch (err) {
|
|
@@ -1115,7 +1069,7 @@ async function main() {
|
|
|
1115
1069
|
// Create documentation system if lanes specified
|
|
1116
1070
|
if (lanes.length > 0) {
|
|
1117
1071
|
// Filter out lanes that the project already covers elsewhere
|
|
1118
|
-
// (issue #97: don't create docs/meetings/ when internal/meetings/
|
|
1072
|
+
// (issue #97: don't create docs/notes/meetings/ when internal/meetings/
|
|
1119
1073
|
// has 12 markdown files). --force bypasses the filter.
|
|
1120
1074
|
|
|
1121
1075
|
const deferredLanes = [];
|
|
@@ -1210,7 +1164,7 @@ async function main() {
|
|
|
1210
1164
|
const seed = await syncFileStateToSql(target, { env: process.env, project: projectName });
|
|
1211
1165
|
if (seed?.status === 'ok' && (seed.documentsSynced || 0) > 0) {
|
|
1212
1166
|
console.log(`\n🔍 Indexed existing project material: ${seed.documentsSynced} doc(s), ${seed.embeddingsSynced || 0} embeddings.`);
|
|
1213
|
-
console.log(` The agent can now compare new intake (
|
|
1167
|
+
console.log(` The agent can now compare new intake (inbox/) against your existing PRDs, ADRs, and notes.`);
|
|
1214
1168
|
}
|
|
1215
1169
|
} catch { /* silent — corpus seeding is best-effort, not a setup blocker */ }
|
|
1216
1170
|
|
|
@@ -1241,9 +1195,9 @@ async function main() {
|
|
|
1241
1195
|
console.log(`${++step}. Edit plan.md`);
|
|
1242
1196
|
console.log(' Add your current work and tasks');
|
|
1243
1197
|
console.log('');
|
|
1244
|
-
if (
|
|
1198
|
+
if (created.includes('inbox/')) {
|
|
1245
1199
|
console.log(`${++step}. Use Intake`);
|
|
1246
|
-
console.log(' Drop files in
|
|
1200
|
+
console.log(' Drop files in inbox/ for processing');
|
|
1247
1201
|
console.log(' Run: construct intake');
|
|
1248
1202
|
console.log('');
|
|
1249
1203
|
}
|
package/lib/init.mjs
CHANGED
|
@@ -77,12 +77,14 @@ function main() {
|
|
|
77
77
|
content: `${JSON.stringify({ version: 1, formality: 3, toneOverride: {}, audienceLexicon: { prefer: [], avoid: [] } }, null, 2)}\n`,
|
|
78
78
|
generator: "construct/init",
|
|
79
79
|
});
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
80
|
+
// Single canonical drop zone (ADR-0045 §C): the visible project-root `inbox/`,
|
|
81
|
+
// covered by the host .gitignore (ADR-0027 §1). Writers stage under the
|
|
82
|
+
// gitignored `inbox/.staging/` and atomically rename in; the watcher consumes
|
|
83
|
+
// only complete top-level files. Machine/runtime intake state lives under the
|
|
84
|
+
// gitignored `.cx/intake/`, created lazily by the queue.
|
|
83
85
|
|
|
84
|
-
fs.mkdirSync(path.join(target, "
|
|
85
|
-
created.push("
|
|
86
|
+
fs.mkdirSync(path.join(target, "inbox", ".staging"), { recursive: true });
|
|
87
|
+
created.push("inbox/");
|
|
86
88
|
|
|
87
89
|
process.stdout.write(`\nConstruct init → ${target}\n\n`);
|
|
88
90
|
if (created.length) {
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* lib/install/desktop-binary-download.mjs — downloads the Construct chat desktop binary.
|
|
3
3
|
*
|
|
4
4
|
* Fetches the platform-matching construct-chat binary from the latest GitHub
|
|
5
|
-
* release and writes it to
|
|
5
|
+
* release and writes it to the bin/ subdir of the XDG state dir. Called by
|
|
6
|
+
* `construct install --scope=user`.
|
|
6
7
|
* Network failures are non-fatal: returns { status: 'skipped', reason } so the
|
|
7
8
|
* rest of setup can proceed without the desktop binary.
|
|
8
9
|
*/
|
|
@@ -10,6 +11,8 @@
|
|
|
10
11
|
import fs from 'node:fs';
|
|
11
12
|
import path from 'node:path';
|
|
12
13
|
|
|
14
|
+
import { stateDir } from '../config/xdg.mjs';
|
|
15
|
+
|
|
13
16
|
const GITHUB_REPO = 'geraldmaron/construct';
|
|
14
17
|
|
|
15
18
|
function assetName() {
|
|
@@ -46,7 +49,7 @@ async function downloadAsset(url, dest, env) {
|
|
|
46
49
|
|
|
47
50
|
// Returns { status: 'downloaded'|'skipped'|'failed' }; 'skipped' and 'failed' are both non-fatal.
|
|
48
51
|
export async function downloadDesktopBinary({ homeDir, env = process.env } = {}) {
|
|
49
|
-
const binDir = path.join(homeDir, '
|
|
52
|
+
const binDir = path.join(stateDir(homeDir), 'bin');
|
|
50
53
|
const dest = path.join(binDir, destBinaryName());
|
|
51
54
|
const name = assetName();
|
|
52
55
|
|
package/lib/intake/daemon.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/intake/daemon.mjs — Continuous intake daemon with safeguards.
|
|
3
3
|
*
|
|
4
|
-
* Polls
|
|
5
|
-
* classifier, and writes packets to `.cx/intake/pending/`.
|
|
6
|
-
* TTL move to `.cx/intake/dead-letter/`; failed
|
|
7
|
-
* the budget then dead-letters with the
|
|
4
|
+
* Polls the canonical project-root `inbox/` for new files, classifies them via
|
|
5
|
+
* the existing intake classifier, and writes packets to `.cx/intake/pending/`.
|
|
6
|
+
* Packets past their TTL move to `.cx/intake/dead-letter/`; failed
|
|
7
|
+
* classification retries up to the budget then dead-letters with the reason.
|
|
8
|
+
*
|
|
9
|
+
* Atomic handoff (ADR-0045 §C): only complete top-level files are enqueued.
|
|
10
|
+
* Dotfiles and the `inbox/.staging/` assembly directory are skipped, so a file
|
|
11
|
+
* a writer is still staging is invisible until it is renamed into `inbox/`.
|
|
8
12
|
*
|
|
9
13
|
* Built on lib/daemons/contract.mjs — every safeguard (bounded lifetime,
|
|
10
14
|
* idle shutdown, heartbeat, killswitch, single-writer lock) applies.
|
|
@@ -12,30 +16,49 @@
|
|
|
12
16
|
|
|
13
17
|
import { readdirSync, statSync, readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from 'node:fs';
|
|
14
18
|
import { join, basename } from 'node:path';
|
|
15
|
-
import { homedir } from 'node:os';
|
|
16
19
|
|
|
17
20
|
import { createDaemon, classifyPacket } from '../daemons/contract.mjs';
|
|
18
21
|
import { memoryCapMbFor } from '../resources/process-budget.mjs';
|
|
22
|
+
import { stateDir } from '../config/xdg.mjs';
|
|
19
23
|
|
|
20
24
|
const KILLSWITCH_ENV = 'CONSTRUCT_INTAKE_DAEMON';
|
|
21
25
|
|
|
22
|
-
function inboxDir(cwd) { return join(cwd, '
|
|
26
|
+
function inboxDir(cwd) { return join(cwd, 'inbox'); }
|
|
23
27
|
function pendingDir(cwd) { return join(cwd, '.cx', 'intake', 'pending'); }
|
|
24
28
|
function deadLetterDir(cwd) { return join(cwd, '.cx', 'intake', 'dead-letter'); }
|
|
25
|
-
function heartbeatPath() { return join(
|
|
29
|
+
function heartbeatPath() { return join(stateDir(), 'intake-daemon.heartbeat'); }
|
|
26
30
|
|
|
27
31
|
function ensureDir(dir) {
|
|
28
32
|
try { mkdirSync(dir, { recursive: true }); } catch { /* ignore */ }
|
|
29
33
|
}
|
|
30
34
|
|
|
35
|
+
// A writer staging into `inbox/.staging/` and renaming into `inbox/` is the
|
|
36
|
+
// atomic handoff. As a backstop for writers that drop in place, a file whose
|
|
37
|
+
// size is still moving between two stats is mid-write — skip it this tick.
|
|
38
|
+
|
|
39
|
+
function isSizeStable(full) {
|
|
40
|
+
try {
|
|
41
|
+
const a = statSync(full);
|
|
42
|
+
const b = statSync(full);
|
|
43
|
+
return a.size === b.size;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
function listInboxFiles(cwd) {
|
|
32
50
|
const dir = inboxDir(cwd);
|
|
33
51
|
if (!existsSync(dir)) return [];
|
|
34
52
|
let entries;
|
|
35
53
|
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return []; }
|
|
54
|
+
|
|
55
|
+
// Only complete top-level files: dotfiles (and the `.staging/` assembly dir)
|
|
56
|
+
// are skipped, so a file a writer is still staging is invisible until rename.
|
|
57
|
+
|
|
36
58
|
return entries
|
|
37
59
|
.filter((e) => e.isFile() && !e.name.startsWith('.'))
|
|
38
|
-
.map((e) => join(dir, e.name))
|
|
60
|
+
.map((e) => join(dir, e.name))
|
|
61
|
+
.filter((full) => isSizeStable(full));
|
|
39
62
|
}
|
|
40
63
|
|
|
41
64
|
/**
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/intake/intake-config.mjs — intake watcher config facade.
|
|
3
3
|
*
|
|
4
|
-
* Reads and writes intake policy via construct.config.json intakePolicy
|
|
5
|
-
*
|
|
4
|
+
* Reads and writes intake policy via construct.config.json intakePolicy, the
|
|
5
|
+
* only config source (single-zone model, ADR-0045 §C).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { shouldCreateCx } from '../project-detection.mjs';
|
|
9
9
|
import {
|
|
10
|
-
loadIntakePolicy,
|
|
11
10
|
resolvedIntakeConfig,
|
|
12
11
|
saveIntakePolicy,
|
|
13
|
-
migrateLegacyIntakeConfig,
|
|
14
12
|
DEFAULT_INTAKE_POLICY,
|
|
15
13
|
} from '../config/intake-policy.mjs';
|
|
16
14
|
import {
|
|
@@ -19,38 +17,22 @@ import {
|
|
|
19
17
|
INTAKE_DEPTH_GUIDANCE,
|
|
20
18
|
describeIntakeDepth,
|
|
21
19
|
} from './constants.mjs';
|
|
22
|
-
import { INTAKE_CONFIG_FILE } from './legacy-paths.mjs';
|
|
23
|
-
import { join } from 'node:path';
|
|
24
20
|
|
|
25
21
|
export {
|
|
26
22
|
INTAKE_DEFAULT_MAX_DEPTH,
|
|
27
23
|
INTAKE_HARD_MAX_DEPTH,
|
|
28
24
|
INTAKE_DEPTH_GUIDANCE,
|
|
29
25
|
describeIntakeDepth,
|
|
30
|
-
INTAKE_CONFIG_FILE,
|
|
31
|
-
migrateLegacyIntakeConfig,
|
|
32
26
|
DEFAULT_INTAKE_POLICY,
|
|
33
27
|
};
|
|
34
28
|
|
|
35
29
|
export const DEFAULT_INTAKE_CONFIG = Object.freeze({
|
|
36
30
|
parentDirs: [],
|
|
37
31
|
maxDepth: INTAKE_DEFAULT_MAX_DEPTH,
|
|
38
|
-
includeProjectInbox: true,
|
|
39
|
-
includeDocsIntake: true,
|
|
40
|
-
includeArchetypeInbox: true,
|
|
41
|
-
includeRootInbox: true,
|
|
42
32
|
});
|
|
43
33
|
|
|
44
|
-
export function intakeConfigPath(rootDir) {
|
|
45
|
-
return join(rootDir, INTAKE_CONFIG_FILE);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
34
|
export function loadIntakeConfig(rootDir, env = process.env) {
|
|
49
|
-
|
|
50
|
-
if (cfg.legacyWarning && process.stderr.isTTY) {
|
|
51
|
-
process.stderr.write(`[intake-config] ${cfg.legacyWarning}\n`);
|
|
52
|
-
}
|
|
53
|
-
return cfg;
|
|
35
|
+
return resolvedIntakeConfig(rootDir, env);
|
|
54
36
|
}
|
|
55
37
|
|
|
56
38
|
export function saveIntakeConfig(rootDir, patch = {}) {
|
|
@@ -60,24 +42,15 @@ export function saveIntakeConfig(rootDir, patch = {}) {
|
|
|
60
42
|
|
|
61
43
|
const policyPatch = {};
|
|
62
44
|
if (patch.maxDepth !== undefined) policyPatch.maxDepth = patch.maxDepth;
|
|
63
|
-
if (patch.includeProjectInbox !== undefined) policyPatch.projectInbox = patch.includeProjectInbox;
|
|
64
|
-
if (patch.includeDocsIntake !== undefined) policyPatch.docsIntake = patch.includeDocsIntake;
|
|
65
|
-
if (patch.includeArchetypeInbox !== undefined || patch.includeRootInbox !== undefined) {
|
|
66
|
-
policyPatch.rootInbox = patch.includeRootInbox ?? patch.includeArchetypeInbox;
|
|
67
|
-
}
|
|
68
45
|
if (Array.isArray(patch.parentDirs)) policyPatch.additionalDirs = patch.parentDirs;
|
|
69
46
|
|
|
70
47
|
const { policy } = saveIntakePolicy(rootDir, policyPatch);
|
|
71
|
-
return
|
|
48
|
+
return policyToConfigShape(policy);
|
|
72
49
|
}
|
|
73
50
|
|
|
74
|
-
function
|
|
51
|
+
function policyToConfigShape(policy) {
|
|
75
52
|
return {
|
|
76
53
|
parentDirs: policy.additionalDirs,
|
|
77
54
|
maxDepth: policy.maxDepth,
|
|
78
|
-
includeProjectInbox: policy.zones.projectInbox,
|
|
79
|
-
includeDocsIntake: policy.zones.docsIntake,
|
|
80
|
-
includeArchetypeInbox: policy.zones.rootInbox,
|
|
81
|
-
includeRootInbox: policy.zones.rootInbox,
|
|
82
55
|
};
|
|
83
56
|
}
|