@doxbrix/doxloop 0.1.5 → 0.2.0
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/CHANGELOG.md +1145 -0
- package/README.md +422 -227
- package/assets/doxbrix-preview.css +77 -15
- package/contracts/agent-events-v1.schema.json +1 -0
- package/contracts/coverage-v1.schema.json +59 -0
- package/contracts/drift-v1.schema.json +1 -0
- package/contracts/evaluation-v1.schema.json +62 -0
- package/contracts/quality-codes-v1.json +1 -0
- package/contracts/quality-report-v1.schema.json +1 -0
- package/contracts/validation-v1.schema.json +1 -0
- package/dist/agent-failure.d.ts +36 -0
- package/dist/agent-failure.js +98 -0
- package/dist/agent-isolation.d.ts +17 -0
- package/dist/agent-isolation.js +94 -0
- package/dist/agent-log.d.ts +197 -0
- package/dist/agent-log.js +1094 -0
- package/dist/agent-process.d.ts +63 -0
- package/dist/agent-process.js +128 -0
- package/dist/agent-reply.d.ts +47 -0
- package/dist/agent-reply.js +315 -0
- package/dist/agents.d.ts +24 -5
- package/dist/agents.js +60 -9
- package/dist/api-coverage.d.ts +11 -0
- package/dist/api-coverage.js +72 -0
- package/dist/application-probe.d.ts +28 -0
- package/dist/application-probe.js +93 -0
- package/dist/application-url.d.ts +14 -0
- package/dist/application-url.js +26 -0
- package/dist/args.js +15 -0
- package/dist/artifact-deploy.d.ts +9 -0
- package/dist/artifact-deploy.js +28 -8
- package/dist/assets.d.ts +72 -0
- package/dist/assets.js +382 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.js +38 -9
- package/dist/author.d.ts +206 -4
- package/dist/author.js +1650 -52
- package/dist/authoring-batches.d.ts +190 -0
- package/dist/authoring-batches.js +489 -0
- package/dist/authoring-postpass.d.ts +79 -0
- package/dist/authoring-postpass.js +1289 -0
- package/dist/authoring-progress.d.ts +95 -0
- package/dist/authoring-progress.js +269 -0
- package/dist/autosync.d.ts +56 -0
- package/dist/autosync.js +442 -0
- package/dist/batch-artifacts.d.ts +81 -0
- package/dist/batch-artifacts.js +412 -0
- package/dist/batch-limits.d.ts +37 -0
- package/dist/batch-limits.js +80 -0
- package/dist/branding.d.ts +41 -0
- package/dist/branding.js +229 -0
- package/dist/bundle-upload.d.ts +26 -0
- package/dist/bundle-upload.js +99 -0
- package/dist/capture-auth.d.ts +95 -0
- package/dist/capture-auth.js +204 -0
- package/dist/capture.d.ts +48 -0
- package/dist/capture.js +20 -1
- package/dist/cli.js +839 -32
- package/dist/content-links.d.ts +10 -0
- package/dist/content-links.js +49 -0
- package/dist/contract-validation.d.ts +4 -0
- package/dist/contract-validation.js +25 -0
- package/dist/coverage-actions.d.ts +8 -0
- package/dist/coverage-actions.js +71 -0
- package/dist/coverage-resolutions.d.ts +16 -0
- package/dist/coverage-resolutions.js +58 -0
- package/dist/db.d.ts +37 -0
- package/dist/db.js +288 -0
- package/dist/demo.d.ts +12 -0
- package/dist/demo.js +122 -0
- package/dist/deploy-credentials.d.ts +7 -0
- package/dist/deploy-credentials.js +76 -0
- package/dist/deploy-targets/github-pages.d.ts +3 -0
- package/dist/deploy-targets/github-pages.js +100 -0
- package/dist/deploy-targets/index.d.ts +8 -0
- package/dist/deploy-targets/index.js +78 -0
- package/dist/deploy-targets/netlify.d.ts +3 -0
- package/dist/deploy-targets/netlify.js +49 -0
- package/dist/deploy-targets/types.d.ts +32 -0
- package/dist/deploy-targets/types.js +2 -0
- package/dist/deploy-targets/vercel.d.ts +3 -0
- package/dist/deploy-targets/vercel.js +95 -0
- package/dist/deploy.d.ts +9 -0
- package/dist/deploy.js +87 -13
- package/dist/deterministic-capture.d.ts +90 -0
- package/dist/deterministic-capture.js +435 -0
- package/dist/direct-edit.d.ts +40 -0
- package/dist/direct-edit.js +159 -0
- package/dist/docs-crawl.d.ts +86 -0
- package/dist/docs-crawl.js +536 -0
- package/dist/docs-site.d.ts +49 -0
- package/dist/docs-site.js +233 -0
- package/dist/doctor.js +8 -0
- package/dist/documentation-collections.d.ts +23 -0
- package/dist/documentation-collections.js +191 -0
- package/dist/documentation-plan.d.ts +253 -0
- package/dist/documentation-plan.js +2563 -0
- package/dist/doxbrix-build.d.ts +19 -0
- package/dist/doxbrix-build.js +167 -0
- package/dist/doxbrix-markdown.d.ts +9 -0
- package/dist/doxbrix-markdown.js +115 -14
- package/dist/drift.d.ts +10 -0
- package/dist/drift.js +164 -0
- package/dist/evaluation.d.ts +46 -0
- package/dist/evaluation.js +113 -0
- package/dist/evidence-pack.d.ts +47 -0
- package/dist/evidence-pack.js +358 -0
- package/dist/evidence.d.ts +25 -0
- package/dist/evidence.js +175 -0
- package/dist/fs.d.ts +8 -2
- package/dist/fs.js +41 -11
- package/dist/generator-api.d.ts +89 -0
- package/dist/generator-preflight.d.ts +27 -0
- package/dist/generator-preflight.js +105 -0
- package/dist/generator-runtime.d.ts +7 -0
- package/dist/generator-runtime.js +17 -1
- package/dist/generators.d.ts +24 -2
- package/dist/generators.js +54 -1
- package/dist/git-delivery.d.ts +17 -0
- package/dist/git-delivery.js +123 -0
- package/dist/globs.d.ts +16 -0
- package/dist/globs.js +65 -0
- package/dist/glossary.d.ts +26 -0
- package/dist/glossary.js +179 -0
- package/dist/history.d.ts +106 -0
- package/dist/history.js +600 -0
- package/dist/html-markdown.d.ts +46 -0
- package/dist/html-markdown.js +423 -0
- package/dist/interactive.js +3 -0
- package/dist/job-events.d.ts +74 -0
- package/dist/job-events.js +377 -0
- package/dist/keep-awake.d.ts +50 -0
- package/dist/keep-awake.js +123 -0
- package/dist/local-source-snapshot.d.ts +20 -0
- package/dist/local-source-snapshot.js +61 -0
- package/dist/mintlify-detect.d.ts +3 -0
- package/dist/mintlify-detect.js +18 -0
- package/dist/mintlify-import.d.ts +75 -0
- package/dist/mintlify-import.js +190 -0
- package/dist/navigation.d.ts +98 -0
- package/dist/navigation.js +310 -0
- package/dist/openapi.d.ts +60 -0
- package/dist/openapi.js +439 -0
- package/dist/page-editor-bridge.d.ts +3 -0
- package/dist/page-editor-bridge.js +109 -0
- package/dist/page-editor-preview.d.ts +10 -0
- package/dist/page-editor-preview.js +55 -0
- package/dist/page-extension.d.ts +9 -0
- package/dist/page-extension.js +15 -0
- package/dist/page-metadata.d.ts +28 -0
- package/dist/page-metadata.js +166 -0
- package/dist/page-operations.d.ts +34 -0
- package/dist/page-operations.js +215 -0
- package/dist/page-routes.d.ts +4 -0
- package/dist/page-routes.js +61 -0
- package/dist/pages.d.ts +20 -0
- package/dist/pages.js +184 -0
- package/dist/plan-generator.d.ts +3 -0
- package/dist/plan-generator.js +21 -0
- package/dist/plan-navigation.d.ts +11 -0
- package/dist/plan-navigation.js +30 -0
- package/dist/planning-captures.d.ts +20 -0
- package/dist/planning-captures.js +143 -0
- package/dist/planning-research.d.ts +135 -0
- package/dist/planning-research.js +472 -0
- package/dist/planning-triage.d.ts +23 -0
- package/dist/planning-triage.js +131 -0
- package/dist/preview.d.ts +24 -0
- package/dist/preview.js +280 -29
- package/dist/project-detect.d.ts +36 -0
- package/dist/project-detect.js +251 -0
- package/dist/project-import.d.ts +54 -0
- package/dist/project-import.js +157 -0
- package/dist/project-lock.d.ts +6 -0
- package/dist/project-lock.js +96 -0
- package/dist/project-registry.d.ts +25 -0
- package/dist/project-registry.js +79 -0
- package/dist/project.d.ts +35 -3
- package/dist/project.js +388 -38
- package/dist/prompts.d.ts +9 -0
- package/dist/prompts.js +25 -0
- package/dist/proposal-replay.d.ts +32 -0
- package/dist/proposal-replay.js +99 -0
- package/dist/quality-claims.d.ts +8 -0
- package/dist/quality-claims.js +168 -0
- package/dist/quality-config.d.ts +5 -0
- package/dist/quality-config.js +84 -0
- package/dist/quality-contract.d.ts +37 -0
- package/dist/quality-contract.js +46 -0
- package/dist/quality-examples.d.ts +4 -0
- package/dist/quality-examples.js +233 -0
- package/dist/quality-gates.d.ts +16 -0
- package/dist/quality-gates.js +192 -0
- package/dist/quality-links.d.ts +7 -0
- package/dist/quality-links.js +149 -0
- package/dist/quality-lint.d.ts +6 -0
- package/dist/quality-lint.js +124 -0
- package/dist/quality-rendered.d.ts +11 -0
- package/dist/quality-rendered.js +222 -0
- package/dist/quality-schema.d.ts +3 -0
- package/dist/quality-schema.js +71 -0
- package/dist/release-notes.d.ts +44 -0
- package/dist/release-notes.js +183 -0
- package/dist/remote-monitor.d.ts +16 -0
- package/dist/remote-monitor.js +74 -0
- package/dist/remote-source.d.ts +34 -0
- package/dist/remote-source.js +426 -0
- package/dist/review-diff.d.ts +82 -0
- package/dist/review-diff.js +400 -0
- package/dist/review-learning.d.ts +11 -0
- package/dist/review-learning.js +60 -0
- package/dist/review-render.d.ts +38 -0
- package/dist/review-render.js +224 -0
- package/dist/review-report.d.ts +9 -0
- package/dist/review-report.js +89 -0
- package/dist/review-ui.d.ts +14 -0
- package/dist/review-ui.js +1248 -0
- package/dist/schedule.d.ts +78 -0
- package/dist/schedule.js +480 -0
- package/dist/screen-capture-provider.d.ts +52 -0
- package/dist/screen-capture-provider.js +218 -0
- package/dist/screenshot-workflow.d.ts +167 -0
- package/dist/screenshot-workflow.js +1237 -0
- package/dist/settings.d.ts +1 -1
- package/dist/settings.js +95 -7
- package/dist/site-export.d.ts +18 -0
- package/dist/site-export.js +87 -0
- package/dist/source-connectors.d.ts +33 -0
- package/dist/source-connectors.js +268 -0
- package/dist/source-discovery.d.ts +132 -0
- package/dist/source-discovery.js +823 -0
- package/dist/source-intelligence.d.ts +9 -0
- package/dist/source-intelligence.js +306 -0
- package/dist/sync-review.d.ts +28 -0
- package/dist/sync-review.js +264 -0
- package/dist/sync-runs.d.ts +192 -0
- package/dist/sync-runs.js +2244 -0
- package/dist/sync.d.ts +35 -0
- package/dist/sync.js +298 -32
- package/dist/text-diff.d.ts +9 -0
- package/dist/text-diff.js +59 -0
- package/dist/types.d.ts +946 -1
- package/dist/ui/assets/doxloop-logo-light-De7Nx7j7.png +0 -0
- package/dist/ui/assets/index-BHBYU2aG.css +1 -0
- package/dist/ui/assets/index-Cq3RPQiC.js +33 -0
- package/dist/ui/index.html +18 -0
- package/dist/ui-server.d.ts +83 -0
- package/dist/ui-server.js +3532 -0
- package/dist/usage-budget.d.ts +28 -0
- package/dist/usage-budget.js +90 -0
- package/dist/validation.d.ts +25 -1
- package/dist/validation.js +312 -26
- package/dist/workspace-tools.d.ts +54 -0
- package/dist/workspace-tools.js +123 -0
- package/docs/agent-compatibility.md +49 -32
- package/docs/ci-and-automation.md +105 -66
- package/docs/doxbrix-http-api.md +8 -1
- package/docs/existing-documentation.md +80 -0
- package/docs/generation-performance.md +108 -0
- package/docs/generator-authoring.md +68 -5
- package/docs/generator-selection.md +50 -13
- package/docs/mintlify-import.md +71 -0
- package/docs/openapi-security.md +25 -0
- package/docs/project-format.md +331 -36
- package/docs/release-quality.md +158 -0
- package/docs/releasing.md +72 -0
- package/docs/review-workflows.md +51 -0
- package/docs/security-model.md +102 -39
- package/docs/troubleshooting.md +226 -93
- package/package.json +46 -15
- package/scripts/test-auto-screenshot.mjs +172 -0
- package/skills/doxloop-authoring/SKILL.md +242 -361
- package/skills/doxloop-authoring/references/existing-documentation.md +94 -0
- package/skills/doxloop-authoring/references/navigation-architecture.md +18 -7
- package/skills/doxloop-authoring/references/page-depth.md +169 -0
- package/skills/doxloop-authoring/references/project-format.md +106 -7
- package/skills/doxloop-authoring/references/quality.md +10 -0
- package/skills/doxloop-authoring/references/screenshot-manifest.md +113 -0
- package/skills/doxloop-authoring/references/screenshots.md +149 -235
- package/skills/doxloop-authoring/references/workflows.md +76 -0
- package/skills/doxloop-doxbrix/SKILL.md +38 -22
- package/skills/doxloop-doxbrix/references/api-endpoints.md +15 -14
- package/skills/doxloop-doxbrix/references/components.md +30 -3
- package/skills/doxloop-doxbrix/references/manifest.md +3 -2
- package/vendor/doxbrix-import/LICENSE +202 -0
- package/vendor/doxbrix-import/README.md +17 -0
- package/vendor/doxbrix-import/UPSTREAM.json +47 -0
- package/vendor/doxbrix-import/dist/docs/frontmatter.d.ts +13 -0
- package/vendor/doxbrix-import/dist/docs/frontmatter.js +83 -0
- package/vendor/doxbrix-import/dist/docs/import.d.ts +21 -0
- package/vendor/doxbrix-import/dist/docs/import.js +147 -0
- package/vendor/doxbrix-import/dist/docs/manifest.d.ts +163 -0
- package/vendor/doxbrix-import/dist/docs/manifest.js +64 -0
- package/vendor/doxbrix-import/dist/docs/project.d.ts +25 -0
- package/vendor/doxbrix-import/dist/docs/project.js +77 -0
- package/vendor/doxbrix-import/dist/docs/starter.d.ts +4 -0
- package/vendor/doxbrix-import/dist/docs/starter.js +11 -0
- package/vendor/doxbrix-import/dist/importer.d.ts +227 -0
- package/vendor/doxbrix-import/dist/importer.js +1567 -0
- package/vendor/doxbrix-import/dist/mintlify-openapi.d.ts +37 -0
- package/vendor/doxbrix-import/dist/mintlify-openapi.js +305 -0
- package/vendor/doxbrix-import/dist/safe-path.d.ts +9 -0
- package/vendor/doxbrix-import/dist/safe-path.js +47 -0
- package/dist/agents.d.ts.map +0 -1
- package/dist/agents.js.map +0 -1
- package/dist/args.d.ts.map +0 -1
- package/dist/args.js.map +0 -1
- package/dist/artifact-deploy.d.ts.map +0 -1
- package/dist/artifact-deploy.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/author.d.ts.map +0 -1
- package/dist/author.js.map +0 -1
- package/dist/capture.d.ts.map +0 -1
- package/dist/capture.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/deploy.d.ts.map +0 -1
- package/dist/deploy.js.map +0 -1
- package/dist/deployment-visibility.d.ts.map +0 -1
- package/dist/deployment-visibility.js.map +0 -1
- package/dist/doctor.d.ts.map +0 -1
- package/dist/doctor.js.map +0 -1
- package/dist/doxbrix-markdown.d.ts.map +0 -1
- package/dist/doxbrix-markdown.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/fs.d.ts.map +0 -1
- package/dist/fs.js.map +0 -1
- package/dist/generator-api.d.ts.map +0 -1
- package/dist/generator-api.js.map +0 -1
- package/dist/generator-manager.d.ts.map +0 -1
- package/dist/generator-manager.js.map +0 -1
- package/dist/generator-runtime.d.ts.map +0 -1
- package/dist/generator-runtime.js.map +0 -1
- package/dist/generators.d.ts.map +0 -1
- package/dist/generators.js.map +0 -1
- package/dist/interactive.d.ts.map +0 -1
- package/dist/interactive.js.map +0 -1
- package/dist/preview.d.ts.map +0 -1
- package/dist/preview.js.map +0 -1
- package/dist/progress.d.ts.map +0 -1
- package/dist/progress.js.map +0 -1
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js.map +0 -1
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js.map +0 -1
- package/dist/settings.d.ts.map +0 -1
- package/dist/settings.js.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
package/dist/autosync.js
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { readFile, writeFile, rename } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { ProjectBusyError, withProjectLock } from './project-lock.js';
|
|
5
|
+
import { agentAuthenticationStatus, chooseAgent } from './agents.js';
|
|
6
|
+
import { computeDrift, computeDriftFromChanges, formatDrift } from './drift.js';
|
|
7
|
+
import { DoxloopError } from './errors.js';
|
|
8
|
+
import { readEvidenceMap } from './evidence.js';
|
|
9
|
+
import { emitJobOutcome } from './job-events.js';
|
|
10
|
+
import { saveProjectSettings, sourceKind } from './project.js';
|
|
11
|
+
import { heading, note, promptConfirm, promptSelect } from './prompts.js';
|
|
12
|
+
import { monitorRemoteSources } from './remote-monitor.js';
|
|
13
|
+
import { appendSyncLog, formatScheduleFrequency, installSchedule, readSyncLog, removeSchedule, scheduleFrequency, scheduleState, } from './schedule.js';
|
|
14
|
+
import { collectSourceChanges, readSyncState, LOCAL_CONTENT_BASELINE } from './sync.js';
|
|
15
|
+
import { createSyncRun, listSyncRuns, pendingRunCount } from './sync-runs.js';
|
|
16
|
+
const MODE_LABELS = {
|
|
17
|
+
check: 'Just tell me — write nothing',
|
|
18
|
+
propose: 'Generate an isolated proposal for review',
|
|
19
|
+
auto: 'Automatically generate an isolated proposal',
|
|
20
|
+
};
|
|
21
|
+
export function replaySyncSetupCommand(sync) {
|
|
22
|
+
const parts = ['doxloop sync setup', `--mode ${sync.mode}`];
|
|
23
|
+
if (sync.branch)
|
|
24
|
+
parts.push(`--branch ${sync.branch}`);
|
|
25
|
+
parts.push(`--on ${sync.on.length > 0 ? sync.on.join(',') : 'manual'}`);
|
|
26
|
+
return parts.join(' ');
|
|
27
|
+
}
|
|
28
|
+
export function parseTriggerList(raw) {
|
|
29
|
+
if (raw.trim().toLowerCase() === 'manual')
|
|
30
|
+
return [];
|
|
31
|
+
if (raw.split(',').filter((value) => value.trim()).length > 1)
|
|
32
|
+
throw new DoxloopError('Choose one schedule per project; multiple triggers are not supported.');
|
|
33
|
+
return raw
|
|
34
|
+
.split(',')
|
|
35
|
+
.map((value) => value.trim())
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.map((value) => {
|
|
38
|
+
if (/^every@\d+[mh]$/.test(value)) {
|
|
39
|
+
const amount = Number(value.match(/\d+/)?.[0]);
|
|
40
|
+
if ((value.endsWith('m') && amount >= 1 && amount <= 59) ||
|
|
41
|
+
(value.endsWith('h') && amount >= 1 && amount <= 24))
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
const daily = /^daily@(\d{2}):(\d{2})$/.exec(value);
|
|
45
|
+
if (daily && Number(daily[1]) <= 23 && Number(daily[2]) <= 59) {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
const weekdays = /^weekdays@(\d{2}):(\d{2})$/.exec(value);
|
|
49
|
+
if (weekdays && Number(weekdays[1]) <= 23 && Number(weekdays[2]) <= 59) {
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
const weekly = /^weekly@(sun|mon|tue|wed|thu|fri|sat)@(\d{2}):(\d{2})$/.exec(value);
|
|
53
|
+
if (weekly && Number(weekly[2]) <= 23 && Number(weekly[3]) <= 59) {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
const monthly = /^monthly@(\d{1,2})@(\d{2}):(\d{2})$/.exec(value);
|
|
57
|
+
if (monthly && Number(monthly[1]) >= 1 && Number(monthly[1]) <= 28 && Number(monthly[2]) <= 23 && Number(monthly[3]) <= 59) {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
throw new DoxloopError(`Invalid --on value "${value}". Use daily@HH:MM, weekdays@HH:MM, weekly@day@HH:MM, monthly@day@HH:MM, every@Nm, every@Nh, or manual.`, 2);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function parseSyncMode(raw) {
|
|
64
|
+
if (raw === 'check' || raw === 'propose' || raw === 'auto')
|
|
65
|
+
return raw;
|
|
66
|
+
throw new DoxloopError(`--mode must be check, propose, or auto.`, 2);
|
|
67
|
+
}
|
|
68
|
+
/** Use provider APIs whenever a remote source is configured. */
|
|
69
|
+
export async function computeConfiguredDrift(root, project) {
|
|
70
|
+
if (!hasRemoteSources(project))
|
|
71
|
+
return computeDrift(root, project);
|
|
72
|
+
const monitored = await monitorRemoteSources(root, project);
|
|
73
|
+
return computeDriftFromChanges(root, project, monitored.changes);
|
|
74
|
+
}
|
|
75
|
+
const NATIVE_SCHEDULER = { install: installSchedule, remove: removeSchedule, state: scheduleState };
|
|
76
|
+
/**
|
|
77
|
+
* Apply a sync configuration: persist it, then make the local machine match it.
|
|
78
|
+
* Installing and removing are the same operation with different inputs, so a
|
|
79
|
+
* project can move between trigger sets without leaving orphaned jobs behind.
|
|
80
|
+
* Local folders are checked in place: a Git checkout by its HEAD commit and
|
|
81
|
+
* working tree, any other folder by the content it held at the last sync.
|
|
82
|
+
* Nothing is fetched, pulled, or written in the source, so a local folder
|
|
83
|
+
* needs no remote to be scheduled.
|
|
84
|
+
*/
|
|
85
|
+
export async function applySyncConfig(root, project, sync, scheduler = NATIVE_SCHEDULER) {
|
|
86
|
+
const frequency = scheduleFrequency(sync.on);
|
|
87
|
+
const sources = sync.branch
|
|
88
|
+
? project.sources.map((source) => source.remote
|
|
89
|
+
? { ...source, remote: { ...source.remote, branch: sync.branch } }
|
|
90
|
+
: source)
|
|
91
|
+
: project.sources;
|
|
92
|
+
await saveProjectSettings(root, { sources, sync });
|
|
93
|
+
const lines = [];
|
|
94
|
+
if (frequency) {
|
|
95
|
+
const state = await scheduler.install(root, frequency);
|
|
96
|
+
lines.push(statusLine('✓', 'Schedule installed', `${formatScheduleFrequency(frequency)} · ${state.label}`));
|
|
97
|
+
for (const source of sources) {
|
|
98
|
+
if (sourceKind(source) !== 'directory' || source.remote)
|
|
99
|
+
continue;
|
|
100
|
+
lines.push(statusLine('✓', 'Local source', `${source.name} is checked in place; nothing is fetched or written there`));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const existing = await scheduler.state(root);
|
|
105
|
+
if (existing.installed) {
|
|
106
|
+
await scheduler.remove(root);
|
|
107
|
+
lines.push(statusLine('✓', 'Schedule removed', existing.label));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
lines.push(statusLine('✓', 'Settings saved', '.doxloop/project.json'));
|
|
111
|
+
return lines;
|
|
112
|
+
}
|
|
113
|
+
const LABEL_WIDTH = 20;
|
|
114
|
+
function statusLine(mark, label, detail) {
|
|
115
|
+
return ` ${mark} ${label.padEnd(LABEL_WIDTH)}${detail}`;
|
|
116
|
+
}
|
|
117
|
+
function pageCount(count) {
|
|
118
|
+
return `${count} stale page${count === 1 ? '' : 's'}`;
|
|
119
|
+
}
|
|
120
|
+
export async function runSyncSetupWizard(options) {
|
|
121
|
+
const { root, project, io } = options;
|
|
122
|
+
const detected = project.sources.find((source) => source.remote)?.remote?.branch;
|
|
123
|
+
const current = project.sync;
|
|
124
|
+
const branch = await promptSelect({
|
|
125
|
+
message: 'Which branch should documentation follow?',
|
|
126
|
+
choices: [
|
|
127
|
+
...(detected
|
|
128
|
+
? [{ value: detected, label: detected, hint: 'detected default branch' }]
|
|
129
|
+
: []),
|
|
130
|
+
{ value: undefined, label: 'Any branch', hint: 'never skip a run' },
|
|
131
|
+
],
|
|
132
|
+
io,
|
|
133
|
+
});
|
|
134
|
+
const on = await promptSelect({
|
|
135
|
+
message: 'When should Doxloop look for drift?',
|
|
136
|
+
choices: [
|
|
137
|
+
{
|
|
138
|
+
value: ['every@15m'],
|
|
139
|
+
label: 'Every 15 minutes',
|
|
140
|
+
hint: 'recommended',
|
|
141
|
+
},
|
|
142
|
+
{ value: ['every@1h'], label: 'Every hour' },
|
|
143
|
+
{ value: ['daily@09:00'], label: 'Daily at 09:00 only' },
|
|
144
|
+
{ value: [], label: 'Only when I run it myself' },
|
|
145
|
+
],
|
|
146
|
+
io,
|
|
147
|
+
});
|
|
148
|
+
const mode = await promptSelect({
|
|
149
|
+
message: 'Documentation is stale. What should happen?',
|
|
150
|
+
choices: [
|
|
151
|
+
{ value: 'check', label: MODE_LABELS.check, hint: 'no agent runs' },
|
|
152
|
+
{ value: 'propose', label: MODE_LABELS.propose, hint: 'recommended' },
|
|
153
|
+
{ value: 'auto', label: MODE_LABELS.auto },
|
|
154
|
+
],
|
|
155
|
+
initialIndex: 1,
|
|
156
|
+
io,
|
|
157
|
+
});
|
|
158
|
+
const sync = {
|
|
159
|
+
...current,
|
|
160
|
+
mode,
|
|
161
|
+
...(branch ? { branch } : {}),
|
|
162
|
+
on,
|
|
163
|
+
};
|
|
164
|
+
heading(io, 'Automatic sync');
|
|
165
|
+
note(io, [
|
|
166
|
+
`Follows: ${project.sources.map((source) => source.path).join(', ') || 'no configured source'}${branch ? ` @ ${branch}` : ''}`,
|
|
167
|
+
`Checks: ${describeTriggers(on)}`,
|
|
168
|
+
`When stale: ${MODE_LABELS[mode]}`,
|
|
169
|
+
mode === 'check'
|
|
170
|
+
? 'Never: starts an agent, deploys, or writes documentation'
|
|
171
|
+
: 'Never: writes to the product repository or deploys',
|
|
172
|
+
].join('\n'));
|
|
173
|
+
io.output.write('\n');
|
|
174
|
+
const proceed = await promptConfirm({ message: 'Set this up?', initial: true, io });
|
|
175
|
+
return proceed ? sync : undefined;
|
|
176
|
+
}
|
|
177
|
+
function describeTriggers(on) {
|
|
178
|
+
if (on.length === 0)
|
|
179
|
+
return 'only when you run doxloop check';
|
|
180
|
+
const parts = [];
|
|
181
|
+
const frequency = scheduleFrequency(on);
|
|
182
|
+
if (frequency)
|
|
183
|
+
parts.push(formatScheduleFrequency(frequency));
|
|
184
|
+
return parts.join(', ');
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Prove the automation can actually run before anyone depends on it. An expired
|
|
188
|
+
* agent sign-in is the most common silent failure for unattended runs, so it is
|
|
189
|
+
* checked here rather than discovered at 09:00 three weeks later.
|
|
190
|
+
*/
|
|
191
|
+
export async function formatSyncStatus(root, project) {
|
|
192
|
+
const sync = project.sync;
|
|
193
|
+
const enabled = sync.on.length > 0;
|
|
194
|
+
const lines = [`Automatic sync: ${enabled ? 'ON' : 'OFF'}`, ''];
|
|
195
|
+
const syncState = await readSyncState(root);
|
|
196
|
+
for (const source of project.sources) {
|
|
197
|
+
if (sourceKind(source) !== 'directory')
|
|
198
|
+
continue;
|
|
199
|
+
if (source.remote) {
|
|
200
|
+
lines.push(statusLine('✓', 'Remote source', `${source.remote.provider}:${source.remote.repository} @ ${source.remote.branch}`));
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const record = syncState.sources[source.name];
|
|
204
|
+
lines.push(statusLine('✓', 'Local source', `${source.name} · ${describeLocalBaseline(record?.commit)}`));
|
|
205
|
+
}
|
|
206
|
+
const schedule = await scheduleState(root);
|
|
207
|
+
const frequency = scheduleFrequency(sync.on);
|
|
208
|
+
const scheduleName = `${frequency ? formatScheduleFrequency(frequency) : 'installed'} · ${schedule.label}`;
|
|
209
|
+
if (!schedule.installed) {
|
|
210
|
+
lines.push(statusLine(frequency ? '✗' : '-', 'Schedule', 'not installed'));
|
|
211
|
+
}
|
|
212
|
+
else if (schedule.registered === false) {
|
|
213
|
+
lines.push(statusLine('✗', 'Schedule', `${scheduleName} · not loaded`));
|
|
214
|
+
}
|
|
215
|
+
else if (schedule.lastExitCode === 1) {
|
|
216
|
+
lines.push(statusLine('!', 'Schedule', `${scheduleName} · last exit 1 (drift or update failure)`));
|
|
217
|
+
}
|
|
218
|
+
else if (schedule.lastExitCode !== undefined && schedule.lastExitCode !== 0) {
|
|
219
|
+
lines.push(statusLine('✗', 'Schedule', `${scheduleName} · last exit ${schedule.lastExitCode}`));
|
|
220
|
+
}
|
|
221
|
+
else if (schedule.running) {
|
|
222
|
+
lines.push(statusLine('✓', 'Schedule', `${scheduleName} · running`));
|
|
223
|
+
}
|
|
224
|
+
else if (schedule.lastExitCode === 0) {
|
|
225
|
+
lines.push(statusLine('✓', 'Schedule', `${scheduleName} · smoke test passed`));
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
lines.push(statusLine('!', 'Schedule', `${scheduleName} · never verified`));
|
|
229
|
+
}
|
|
230
|
+
if (sync.mode === 'check') {
|
|
231
|
+
lines.push(statusLine('-', 'Agent', 'not needed in check mode'));
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
lines.push(await agentStatusLine(project));
|
|
235
|
+
}
|
|
236
|
+
const map = await readEvidenceMap(root);
|
|
237
|
+
lines.push(map
|
|
238
|
+
? statusLine('✓', 'Evidence map', `${Object.keys(map.pages).length} page${Object.keys(map.pages).length === 1 ? '' : 's'} tracked`)
|
|
239
|
+
: statusLine('✗', 'Evidence map', 'missing — run `doxloop update` to build it'));
|
|
240
|
+
const log = await readSyncLog(root, 1);
|
|
241
|
+
lines.push('', ` Last run ${log[0] ?? 'never'}`);
|
|
242
|
+
const pending = pendingRunCount(await listSyncRuns(root));
|
|
243
|
+
lines.push(` Reviews ${pending === 0 ? 'none pending' : `${pending} pending · open with: doxloop sync review --open`}`);
|
|
244
|
+
const drift = await computeConfiguredDrift(root, project);
|
|
245
|
+
lines.push(` Drift now ${drift.status === 'current'
|
|
246
|
+
? 'none'
|
|
247
|
+
: drift.status === 'stale'
|
|
248
|
+
? `${drift.pages.length} page${drift.pages.length === 1 ? '' : 's'} stale`
|
|
249
|
+
: 'cannot be determined'}`);
|
|
250
|
+
if (!enabled)
|
|
251
|
+
lines.push('', 'Enable it with: doxloop sync setup');
|
|
252
|
+
return lines.join('\n');
|
|
253
|
+
}
|
|
254
|
+
function describeLocalBaseline(commit) {
|
|
255
|
+
if (!commit)
|
|
256
|
+
return 'checked in place; no baseline recorded yet';
|
|
257
|
+
if (commit === LOCAL_CONTENT_BASELINE)
|
|
258
|
+
return 'compared by content (no Git history)';
|
|
259
|
+
return `Git HEAD, baseline ${commit.slice(0, 12)}`;
|
|
260
|
+
}
|
|
261
|
+
async function agentStatusLine(project) {
|
|
262
|
+
try {
|
|
263
|
+
const agent = await chooseAgent(project.defaultAgent);
|
|
264
|
+
const status = await agentAuthenticationStatus(agent);
|
|
265
|
+
if (status.status === 'authenticated') {
|
|
266
|
+
return statusLine('✓', 'Agent', `${agent.name}, signed in`);
|
|
267
|
+
}
|
|
268
|
+
if (status.status === 'unauthenticated') {
|
|
269
|
+
return statusLine('✗', 'Agent', status.detail);
|
|
270
|
+
}
|
|
271
|
+
return statusLine('-', 'Agent', `${agent.name}, sign-in not verifiable`);
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
return statusLine('✗', 'Agent', error instanceof Error ? error.message : String(error));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
export async function runSyncNow(options) {
|
|
278
|
+
try {
|
|
279
|
+
return await withProjectLock(options.root, 'monitor', () => withProjectLock(options.root, 'authoring', () => runSyncNowLocked(options), 0), 0);
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
if (!(error instanceof ProjectBusyError))
|
|
283
|
+
throw error;
|
|
284
|
+
await appendSyncLog(options.root, `skipped: ${error.message}`);
|
|
285
|
+
emitJobOutcome({ kind: 'sync', status: 'skipped', message: error.message });
|
|
286
|
+
return 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async function runSyncNowLocked(options) {
|
|
290
|
+
const { root, project } = options;
|
|
291
|
+
const frequency = scheduleFrequency(project.sync.on);
|
|
292
|
+
if (options.trigger === 'schedule' && frequency?.kind === 'interval') {
|
|
293
|
+
const clockPath = join(root, '.doxloop', 'monitor-interval.json');
|
|
294
|
+
let last = 0;
|
|
295
|
+
try {
|
|
296
|
+
last = Number(JSON.parse(await readFile(clockPath, 'utf8')).startedAt);
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
if (error.code !== 'ENOENT')
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
const now = Date.now();
|
|
303
|
+
if (last > 0 && now >= last && now - last < frequency.minutes * 60_000)
|
|
304
|
+
return 0;
|
|
305
|
+
const temporary = `${clockPath}.${randomUUID()}.tmp`;
|
|
306
|
+
await writeFile(temporary, JSON.stringify({ startedAt: now }), { mode: 0o600 });
|
|
307
|
+
await rename(temporary, clockPath);
|
|
308
|
+
}
|
|
309
|
+
const write = (text) => {
|
|
310
|
+
if (!options.quiet)
|
|
311
|
+
process.stdout.write(text);
|
|
312
|
+
};
|
|
313
|
+
const monitored = hasRemoteSources(project)
|
|
314
|
+
? await monitorRemoteSources(root, project)
|
|
315
|
+
: undefined;
|
|
316
|
+
const drift = monitored
|
|
317
|
+
? await computeDriftFromChanges(root, project, monitored.changes)
|
|
318
|
+
: await computeDrift(root, project);
|
|
319
|
+
const outcome = (status, message, extra = {}) => emitJobOutcome({ kind: 'sync', status, message, pages: drift.pages.length, ...extra });
|
|
320
|
+
if (drift.status === 'current' && !options.authoring) {
|
|
321
|
+
await appendSyncLog(root, 'check: no reader-visible changes');
|
|
322
|
+
write(`${formatDrift(drift)}\n`);
|
|
323
|
+
outcome('current', 'No reader-visible source changes since the last check.');
|
|
324
|
+
return 0;
|
|
325
|
+
}
|
|
326
|
+
write(`${formatDrift(drift)}\n`);
|
|
327
|
+
// Without a baseline (no documentation generated or accepted yet) nothing
|
|
328
|
+
// can be stale, so drafting an update would only spend agent time. Report
|
|
329
|
+
// it; the first accepted update records the baseline.
|
|
330
|
+
if (drift.status === 'unknown' && drift.pages.length === 0 && !options.authoring) {
|
|
331
|
+
const message = drift.notes.some((note) => /no sync baseline/i.test(note))
|
|
332
|
+
? 'No documentation has been generated or accepted from these sources yet, so there is nothing to compare. Generate the documentation from the plan first.'
|
|
333
|
+
: drift.notes[0] ?? 'Doxloop could not determine whether the documentation is current.';
|
|
334
|
+
await appendSyncLog(root, `check: drift unknown, no update drafted`);
|
|
335
|
+
outcome('unknown', message);
|
|
336
|
+
return 1;
|
|
337
|
+
}
|
|
338
|
+
if (project.sync.mode === 'check' && !options.authoring) {
|
|
339
|
+
await appendSyncLog(root, `check: ${pageCount(drift.pages.length)}, reporting only`);
|
|
340
|
+
if (drift.status === 'stale')
|
|
341
|
+
outcome('stale', `${pageCount(drift.pages.length)} · monitoring is in check mode, so no proposal was drafted.`);
|
|
342
|
+
else
|
|
343
|
+
outcome('unknown', drift.notes[0] ?? 'Doxloop could not determine whether the documentation is current.');
|
|
344
|
+
return 1;
|
|
345
|
+
}
|
|
346
|
+
const pending = !options.authoring && (await listSyncRuns(root)).some((run) => ['generating', 'awaiting-review', 'partially-applied', 'conflicted'].includes(run.status) && !run.archivedAt);
|
|
347
|
+
if (pending) {
|
|
348
|
+
const message = 'An existing proposal needs review. Accept, reject, or archive it before monitoring drafts another update.';
|
|
349
|
+
await appendSyncLog(root, `skipped: ${message}`);
|
|
350
|
+
outcome('skipped', message);
|
|
351
|
+
return 1;
|
|
352
|
+
}
|
|
353
|
+
let blocked = await authoringBlocker(root, project);
|
|
354
|
+
if (!blocked && !options.author) {
|
|
355
|
+
try {
|
|
356
|
+
const agent = await chooseAgent(options.authoring?.agent ?? project.defaultAgent);
|
|
357
|
+
const authentication = await agentAuthenticationStatus(agent);
|
|
358
|
+
if (authentication.status === 'unauthenticated' || (options.trigger === 'schedule' && authentication.status !== 'authenticated'))
|
|
359
|
+
blocked = `sign in to ${agent.name} before unattended authoring: ${authentication.detail}`;
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
blocked = error instanceof Error ? error.message : String(error);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (blocked) {
|
|
366
|
+
await appendSyncLog(root, `skipped: ${blocked}`);
|
|
367
|
+
write(`\nSkipping the documentation update: ${blocked}\n`);
|
|
368
|
+
outcome('skipped', `${pageCount(drift.pages.length)} · the update was skipped because ${blocked}`);
|
|
369
|
+
return 1;
|
|
370
|
+
}
|
|
371
|
+
await reserveRun(root);
|
|
372
|
+
await appendSyncLog(root, `proposal: starting for ${pageCount(drift.pages.length)}`);
|
|
373
|
+
const changes = monitored?.changes ?? await collectSourceChanges(root, project.sources);
|
|
374
|
+
const proposal = await createSyncRun({
|
|
375
|
+
root,
|
|
376
|
+
project,
|
|
377
|
+
drift,
|
|
378
|
+
sourceChanges: changes,
|
|
379
|
+
...(monitored ? { authoringSources: monitored.project.sources } : {}),
|
|
380
|
+
...(monitored ? { nextSyncState: monitored.nextState } : {}),
|
|
381
|
+
...(options.trigger ? { trigger: options.trigger } : {}),
|
|
382
|
+
...(options.author ? { author: options.author } : {}),
|
|
383
|
+
...(options.authoring ? { authoring: options.authoring } : {}),
|
|
384
|
+
});
|
|
385
|
+
if (proposal.status === 'failed') {
|
|
386
|
+
await appendSyncLog(root, `proposal: ${proposal.id} failed: ${proposal.error ?? 'unknown error'}`);
|
|
387
|
+
write(`\nDocumentation proposal failed: ${proposal.error ?? 'unknown error'}\n`);
|
|
388
|
+
outcome('failed', `The documentation proposal failed: ${proposal.error ?? 'unknown error'}`, { proposalId: proposal.id });
|
|
389
|
+
return 1;
|
|
390
|
+
}
|
|
391
|
+
await appendSyncLog(root, `proposal: ${proposal.id} ready for review with ${proposal.changes.length} changed files`);
|
|
392
|
+
write(`\nProposal ${proposal.id} is ready. The actual documentation is unchanged.\nReview and accept changes with:\n doxloop sync review --open\n`);
|
|
393
|
+
outcome('proposal', `${options.authoring ? 'The requested update' : pageCount(drift.pages.length)} · proposal ${proposal.id} is ready for review with ${proposal.changes.length} changed file${proposal.changes.length === 1 ? '' : 's'}.`, { proposalId: proposal.id });
|
|
394
|
+
return 0;
|
|
395
|
+
}
|
|
396
|
+
/** A reason authoring must not start, or undefined when it may proceed. */
|
|
397
|
+
async function authoringBlocker(root, project) {
|
|
398
|
+
const limit = project.sync.budget?.maxRunsPerDay;
|
|
399
|
+
if (limit !== undefined && (await runsToday(root)) >= limit) {
|
|
400
|
+
return `the configured budget of ${limit} run${limit === 1 ? '' : 's'} per day is spent.`;
|
|
401
|
+
}
|
|
402
|
+
return undefined;
|
|
403
|
+
}
|
|
404
|
+
async function budgetLedger(root) {
|
|
405
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
406
|
+
try {
|
|
407
|
+
const ledger = JSON.parse(await readFile(join(root, '.doxloop', 'monitor-budget.json'), 'utf8'));
|
|
408
|
+
if (!Number.isInteger(ledger.count) || ledger.count < 0)
|
|
409
|
+
throw new DoxloopError('Monitoring budget state is invalid. Repair it before starting another run.');
|
|
410
|
+
return ledger.date === date ? ledger : { date, count: 0 };
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
if (error.code !== 'ENOENT')
|
|
414
|
+
throw error;
|
|
415
|
+
// Migrate starts recorded before the durable ledger was introduced.
|
|
416
|
+
const log = await readSyncLog(root, 100_000);
|
|
417
|
+
return { date, count: log.filter((line) => line.startsWith(date) && (line.includes('proposal: starting') || line.includes('update: starting'))).length };
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async function runsToday(root) { return (await budgetLedger(root)).count; }
|
|
421
|
+
async function reserveRun(root) {
|
|
422
|
+
const ledger = await budgetLedger(root);
|
|
423
|
+
const path = join(root, '.doxloop', 'monitor-budget.json');
|
|
424
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
425
|
+
await writeFile(temporary, JSON.stringify({ ...ledger, count: ledger.count + 1 }), { mode: 0o600 });
|
|
426
|
+
await rename(temporary, path);
|
|
427
|
+
}
|
|
428
|
+
export async function disableSync(root, project) {
|
|
429
|
+
const lines = [];
|
|
430
|
+
const schedule = await scheduleState(root);
|
|
431
|
+
if (schedule.installed) {
|
|
432
|
+
await removeSchedule(root);
|
|
433
|
+
lines.push(statusLine('✓', 'Schedule removed', schedule.label));
|
|
434
|
+
}
|
|
435
|
+
await saveProjectSettings(root, { sync: { ...project.sync, on: [] } });
|
|
436
|
+
lines.push(statusLine('✓', 'Settings kept', 're-enable with: doxloop sync setup'));
|
|
437
|
+
return lines;
|
|
438
|
+
}
|
|
439
|
+
function hasRemoteSources(project) {
|
|
440
|
+
return project.sources.some((source) => sourceKind(source) === 'directory' && source.remote !== undefined);
|
|
441
|
+
}
|
|
442
|
+
//# sourceMappingURL=autosync.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { AuthoringBatch } from './authoring-batches.js';
|
|
2
|
+
import type { DocumentationPlan } from './types.js';
|
|
3
|
+
export interface BatchArtifacts {
|
|
4
|
+
/** Project-relative plan slice for the batch. */
|
|
5
|
+
slice: string;
|
|
6
|
+
/** Project-relative manifest slice, when the batch has screenshot guides. */
|
|
7
|
+
manifest?: string;
|
|
8
|
+
/** Project-relative evidence-map slice. */
|
|
9
|
+
evidence: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function batchArtifactPaths(index: number): BatchArtifacts;
|
|
12
|
+
/** Write the slices a batch session reads and fills in. */
|
|
13
|
+
export declare function writeBatchArtifacts(workspace: string, plan: DocumentationPlan, batch: AuthoringBatch, slice: Record<string, unknown>,
|
|
14
|
+
/** Project-relative page files of the batch, for the evidence slice's keys. */
|
|
15
|
+
pageFiles?: string[]): Promise<BatchArtifacts>;
|
|
16
|
+
export interface BatchMergeReport {
|
|
17
|
+
guides: number;
|
|
18
|
+
evidencePages: number;
|
|
19
|
+
problems: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Fold a finished batch's manifest and evidence slices back into the workspace files. */
|
|
22
|
+
export declare function mergeBatchArtifacts(workspace: string, artifacts: BatchArtifacts): Promise<BatchMergeReport>;
|
|
23
|
+
/** Run `worker` over `items` with at most `concurrency` in flight, preserving nothing about order. */
|
|
24
|
+
export declare function runPool<T>(items: readonly T[], concurrency: number, worker: (item: T) => Promise<void>, shouldStop?: () => boolean): Promise<void>;
|
|
25
|
+
/** A promise-chained mutex for the workspace files several sessions share. */
|
|
26
|
+
export declare function createLock(): <T>(task: () => Promise<T>) => Promise<T>;
|
|
27
|
+
export interface CaptureNormalization {
|
|
28
|
+
/** Steps whose saved image exists and passed the image checks. */
|
|
29
|
+
verified: string[];
|
|
30
|
+
/** Steps reset to planned, with the reason. */
|
|
31
|
+
reset: Array<{
|
|
32
|
+
step: string;
|
|
33
|
+
reason: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Decide each step's status from what is on disk, not from the word the agent
|
|
38
|
+
* wrote. Capture sessions have recorded `captured`, `done`, and `ok`; every
|
|
39
|
+
* such row failed the manifest check, and a real run retook thirteen images
|
|
40
|
+
* it already had. A step with a readable, non-blank PNG of usable size at its
|
|
41
|
+
* recorded path is verified; a step that claims a file it does not have goes
|
|
42
|
+
* back to planned so the retake knows about it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeCaptureSteps(guides: Array<{
|
|
45
|
+
page: string;
|
|
46
|
+
steps: unknown[];
|
|
47
|
+
}>, imageAt: (file: string) => {
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
blank: boolean;
|
|
51
|
+
} | undefined): CaptureNormalization;
|
|
52
|
+
/** Normalize the steps of a manifest file (a capture slice or the main manifest) against the images in the workspace. */
|
|
53
|
+
export declare function normalizeCaptureManifest(workspace: string, manifestFile: string): Promise<CaptureNormalization>;
|
|
54
|
+
/** Files a session read, grouped by configured source name, source-relative. */
|
|
55
|
+
export type SessionSourceReads = Map<string, Set<string>>;
|
|
56
|
+
/**
|
|
57
|
+
* Map the absolute paths a session opened to the configured sources they
|
|
58
|
+
* live in. Only files under a source count; a page the agent re-read or a
|
|
59
|
+
* cache file is not evidence.
|
|
60
|
+
*/
|
|
61
|
+
export declare function attributeReadsToSources(paths: Iterable<string>, sources: ReadonlyArray<{
|
|
62
|
+
name: string;
|
|
63
|
+
path: string;
|
|
64
|
+
}>, workspace: string): SessionSourceReads;
|
|
65
|
+
/** Paths named in a tool call: Read/Grep targets and the file arguments of a shell command. */
|
|
66
|
+
export declare function pathsInToolCall(tool: string, input: Record<string, unknown>): string[];
|
|
67
|
+
/**
|
|
68
|
+
* Doxloop owns the evidence map. An agent-written slice is input, not truth:
|
|
69
|
+
* its claims are kept, its source paths are kept only where the file exists,
|
|
70
|
+
* and the plan's citations plus the files this session actually read are
|
|
71
|
+
* always present. A page whose entry names no path it can be checked
|
|
72
|
+
* against is `inferred`, whatever the agent wrote.
|
|
73
|
+
*/
|
|
74
|
+
export declare function reconcileEvidenceSlice(workspace: string, sliceFile: string, plan: Pick<DocumentationPlan, 'target'>, pages: DocumentationPlan['pages'], sources: ReadonlyArray<{
|
|
75
|
+
name: string;
|
|
76
|
+
path: string;
|
|
77
|
+
}>, reads: SessionSourceReads): Promise<{
|
|
78
|
+
pages: number;
|
|
79
|
+
droppedPaths: number;
|
|
80
|
+
}>;
|
|
81
|
+
//# sourceMappingURL=batch-artifacts.d.ts.map
|