create-quiver 0.12.0 → 0.13.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 +52 -0
- package/README.md +65 -25
- package/README_FOR_AI.md +36 -29
- package/ROADMAP.md +22 -3
- package/docs/AI_ONBOARDING_PROMPT.md.template +7 -1
- package/docs/COMMANDS.md.template +53 -20
- package/docs/STATUS.md.template +5 -1
- package/docs/WORKFLOW.md.template +13 -11
- package/package.json +10 -3
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EVIDENCE_REPORT.md +293 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EXECUTION_PLAN.md +58 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/SPEC.md +242 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/STATUS.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/pr.md +77 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/slice.json +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/slice.json +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/slice.json +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/EVIDENCE_REPORT.md +208 -0
- package/specs/quiver-v26-0121-smoke-hardening/EXECUTION_PLAN.md +57 -0
- package/specs/quiver-v26-0121-smoke-hardening/SPEC.md +137 -0
- package/specs/quiver-v26-0121-smoke-hardening/STATUS.md +32 -0
- package/specs/quiver-v26-0121-smoke-hardening/pr.md +96 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/slice.json +73 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/slice.json +76 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/slice.json +75 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/slice.json +84 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/slice.json +82 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/slice.json +92 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/AUDIT_V24_V25_V26.md +67 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/COMMAND_CONTRACTS.md +125 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/COVERAGE_MATRIX.md +74 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/EVIDENCE_REPORT.md +179 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/EXECUTION_PLAN.md +71 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/SPEC.md +176 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/STATUS.md +37 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/pr.md +132 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/slice.json +75 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/slice.json +79 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/slice.json +75 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/slice.json +78 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/slice.json +77 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/slice.json +84 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/slice.json +99 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/slice.json +88 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/slice.json +85 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/slice.json +91 -0
- package/src/create-quiver/commands/ai.js +652 -27
- package/src/create-quiver/commands/flow.js +58 -9
- package/src/create-quiver/commands/graph.js +11 -9
- package/src/create-quiver/commands/plan.js +7 -16
- package/src/create-quiver/commands/spec.js +282 -0
- package/src/create-quiver/index.js +409 -31
- package/src/create-quiver/lib/actionable-error.js +27 -0
- package/src/create-quiver/lib/agent-profiles.js +16 -4
- package/src/create-quiver/lib/ai/artifacts.js +318 -0
- package/src/create-quiver/lib/ai/context-packs.js +4 -0
- package/src/create-quiver/lib/ai/execution-plan.js +16 -1
- package/src/create-quiver/lib/ai/executor.js +272 -21
- package/src/create-quiver/lib/ai/export-state.js +679 -0
- package/src/create-quiver/lib/ai/github.js +162 -2
- package/src/create-quiver/lib/ai/onboarding-template.js +215 -2
- package/src/create-quiver/lib/ai/plan-review.js +7 -2
- package/src/create-quiver/lib/ai/providers.js +4 -3
- package/src/create-quiver/lib/ai/run-state.js +414 -0
- package/src/create-quiver/lib/ai/spec-generator.js +84 -13
- package/src/create-quiver/lib/ai/spec-templates.js +150 -21
- package/src/create-quiver/lib/analyze.js +2 -2
- package/src/create-quiver/lib/approvals.js +36 -5
- package/src/create-quiver/lib/demo.js +189 -14
- package/src/create-quiver/lib/doctor.js +154 -0
- package/src/create-quiver/lib/git.js +40 -1
- package/src/create-quiver/lib/handoff.js +123 -12
- package/src/create-quiver/lib/init-docs.js +35 -13
- package/src/create-quiver/lib/init-layout.js +9 -0
- package/src/create-quiver/lib/json.js +53 -3
- package/src/create-quiver/lib/lifecycle.js +52 -3
- package/src/create-quiver/lib/locks.js +134 -0
- package/src/create-quiver/lib/package-safety.js +7 -0
- package/src/create-quiver/lib/paths.js +74 -0
- package/src/create-quiver/lib/project-scan.js +74 -0
- package/src/create-quiver/lib/project-state-resolver.js +236 -0
- package/src/create-quiver/lib/readiness.js +66 -10
- package/src/create-quiver/lib/scope.js +52 -8
- package/src/create-quiver/lib/slice-graph.js +138 -38
- package/src/create-quiver/lib/slice.js +14 -5
- package/src/create-quiver/lib/spec-worktrees.js +129 -32
- package/src/create-quiver/lib/statuses.js +115 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function formatActionableError({ failure, impact, fix, nextCommand } = {}) {
|
|
2
|
+
const lines = [`create-quiver: ${String(failure || 'operation failed').trim()}`];
|
|
3
|
+
|
|
4
|
+
if (impact) {
|
|
5
|
+
lines.push(`Impact: ${String(impact).trim()}`);
|
|
6
|
+
}
|
|
7
|
+
if (fix) {
|
|
8
|
+
lines.push(`Fix: ${String(fix).trim()}`);
|
|
9
|
+
}
|
|
10
|
+
if (nextCommand) {
|
|
11
|
+
lines.push(`Next command: ${String(nextCommand).trim()}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return lines.join('\n');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createActionableError(code, fields = {}, details = {}) {
|
|
18
|
+
const error = new Error(formatActionableError(fields));
|
|
19
|
+
error.code = code;
|
|
20
|
+
error.details = details;
|
|
21
|
+
return error;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
createActionableError,
|
|
26
|
+
formatActionableError,
|
|
27
|
+
};
|
|
@@ -4,7 +4,7 @@ const path = require('node:path');
|
|
|
4
4
|
const { assertSupportedProvider, formatProviderList } = require('./ai/providers');
|
|
5
5
|
const { quiverInternalPaths } = require('./init-layout');
|
|
6
6
|
|
|
7
|
-
const AGENT_PROFILE_ROLES = Object.freeze(['planner', 'executor', 'reviewer', '
|
|
7
|
+
const AGENT_PROFILE_ROLES = Object.freeze(['planner', 'executor', 'reviewer', 'doctor']);
|
|
8
8
|
const PROFILE_STATE_VERSION = 1;
|
|
9
9
|
|
|
10
10
|
function formatError(message) {
|
|
@@ -95,6 +95,16 @@ function listAgentProfiles(projectRoot) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function setAgentProfile(projectRoot, role, options = {}) {
|
|
98
|
+
const next = buildAgentProfileState(projectRoot, role, options);
|
|
99
|
+
|
|
100
|
+
const filePath = writeAgentProfiles(projectRoot, next.state);
|
|
101
|
+
return {
|
|
102
|
+
filePath,
|
|
103
|
+
profile: next.profile,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildAgentProfileState(projectRoot, role, options = {}) {
|
|
98
108
|
const normalizedRole = normalizeAgentProfileRole(role);
|
|
99
109
|
const provider = assertSupportedProvider(options.provider);
|
|
100
110
|
const model = normalizeOptionalText(options.model, 'model');
|
|
@@ -102,7 +112,7 @@ function setAgentProfile(projectRoot, role, options = {}) {
|
|
|
102
112
|
const context = normalizeOptionalText(options.context, 'context');
|
|
103
113
|
const state = readAgentProfiles(projectRoot);
|
|
104
114
|
const current = state.profiles[normalizedRole] || {};
|
|
105
|
-
const now = new Date().toISOString();
|
|
115
|
+
const now = options.now instanceof Date ? options.now.toISOString() : new Date().toISOString();
|
|
106
116
|
const profile = {
|
|
107
117
|
role: normalizedRole,
|
|
108
118
|
provider,
|
|
@@ -119,10 +129,11 @@ function setAgentProfile(projectRoot, role, options = {}) {
|
|
|
119
129
|
};
|
|
120
130
|
state.updated_at = now;
|
|
121
131
|
|
|
122
|
-
const filePath = writeAgentProfiles(projectRoot, state);
|
|
123
132
|
return {
|
|
124
|
-
|
|
133
|
+
action: current.provider ? 'update' : 'create',
|
|
134
|
+
filePath: agentProfilesPath(projectRoot),
|
|
125
135
|
profile,
|
|
136
|
+
state,
|
|
126
137
|
};
|
|
127
138
|
}
|
|
128
139
|
|
|
@@ -139,6 +150,7 @@ module.exports = {
|
|
|
139
150
|
PROFILE_STATE_VERSION,
|
|
140
151
|
agentProfilesPath,
|
|
141
152
|
formatProviderList,
|
|
153
|
+
buildAgentProfileState,
|
|
142
154
|
getAgentProfile,
|
|
143
155
|
listAgentProfiles,
|
|
144
156
|
normalizeAgentProfileRole,
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const os = require('node:os');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
|
|
5
|
+
const { redactSecrets } = require('../evidence');
|
|
6
|
+
const { quiverInternalPaths } = require('../init-layout');
|
|
7
|
+
|
|
8
|
+
const RAW_ARTIFACT_SCHEMA_VERSION = 1;
|
|
9
|
+
const DEFAULT_MAX_PROVIDER_PROMPT_BYTES = 1024 * 1024;
|
|
10
|
+
const DEFAULT_MAX_REVISION_INPUT_BYTES = 400 * 1024;
|
|
11
|
+
const DEFAULT_COMPACTED_REVISION_INPUT_BYTES = 120 * 1024;
|
|
12
|
+
|
|
13
|
+
const IMPORTANT_REVISION_LINE = /\b(acceptance|criteri[ao]s?|decision|decisi[o\u00f3]n|risk|riesgo|file|archivo|changed|cambio|scope|alcance|validation|validaci[o\u00f3]n|test|blocker|bloque|dependency|dependencia|assumption|supuesto|pending|pendiente|error|rollback|evidence|evidencia)\b/i;
|
|
14
|
+
const PROVIDER_LOG_LINE = /^\s*(?:\[?(?:debug|info|notice|trace|warn|warning)\]?[:\s-]|(?:codex|claude|gemini)\b.*(?:provider|model|prompt|token|running|loading|thinking)|(?:using|loading)\s+(?:model|provider)\b|prompt\s+(?:length|transport)\s*:)/i;
|
|
15
|
+
|
|
16
|
+
function formatError(message) {
|
|
17
|
+
return `create-quiver: ${message}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function byteLength(value) {
|
|
21
|
+
return Buffer.byteLength(String(value || ''), 'utf8');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeText(value) {
|
|
25
|
+
return String(value || '')
|
|
26
|
+
.replace(/\r\n/g, '\n')
|
|
27
|
+
.replace(/\r/g, '\n')
|
|
28
|
+
.replace(/\u001b\[[0-9;]*m/g, '');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function escapeRegExp(value) {
|
|
32
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function redactSensitiveLocalValues(text, options = {}) {
|
|
36
|
+
let result = redactSecrets(text);
|
|
37
|
+
const replacements = [];
|
|
38
|
+
|
|
39
|
+
if (options.projectRoot) {
|
|
40
|
+
replacements.push({
|
|
41
|
+
value: path.resolve(options.projectRoot),
|
|
42
|
+
label: '[PROJECT_ROOT]',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (os.homedir()) {
|
|
47
|
+
replacements.push({
|
|
48
|
+
value: os.homedir(),
|
|
49
|
+
label: '[HOME]',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const replacement of replacements) {
|
|
54
|
+
if (!replacement.value) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
result = result.replace(new RegExp(escapeRegExp(replacement.value), 'g'), replacement.label);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function normalizePositiveInteger(value, fallback) {
|
|
64
|
+
if (value === undefined || value === null || value === '') {
|
|
65
|
+
return fallback;
|
|
66
|
+
}
|
|
67
|
+
const parsed = Number(value);
|
|
68
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
69
|
+
return fallback;
|
|
70
|
+
}
|
|
71
|
+
return Math.floor(parsed);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function resolveAiArtifactLimits(options = {}) {
|
|
75
|
+
return {
|
|
76
|
+
maxProviderPromptBytes: normalizePositiveInteger(
|
|
77
|
+
options.maxProviderPromptBytes ?? process.env.QUIVER_AI_MAX_PROMPT_BYTES,
|
|
78
|
+
DEFAULT_MAX_PROVIDER_PROMPT_BYTES,
|
|
79
|
+
),
|
|
80
|
+
maxRevisionInputBytes: normalizePositiveInteger(
|
|
81
|
+
options.maxRevisionInputBytes ?? process.env.QUIVER_AI_MAX_REVISION_INPUT_BYTES,
|
|
82
|
+
DEFAULT_MAX_REVISION_INPUT_BYTES,
|
|
83
|
+
),
|
|
84
|
+
compactedRevisionInputBytes: normalizePositiveInteger(
|
|
85
|
+
options.compactedRevisionInputBytes ?? process.env.QUIVER_AI_COMPACTED_REVISION_INPUT_BYTES,
|
|
86
|
+
DEFAULT_COMPACTED_REVISION_INPUT_BYTES,
|
|
87
|
+
),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function stripPromptEcho(text, prompt) {
|
|
92
|
+
const normalizedText = normalizeText(text);
|
|
93
|
+
const normalizedPrompt = normalizeText(prompt).trim();
|
|
94
|
+
|
|
95
|
+
if (!normalizedPrompt || normalizedPrompt.length < 80) {
|
|
96
|
+
return normalizedText;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const directIndex = normalizedText.indexOf(normalizedPrompt);
|
|
100
|
+
if (directIndex >= 0) {
|
|
101
|
+
return `${normalizedText.slice(0, directIndex)}${normalizedText.slice(directIndex + normalizedPrompt.length)}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return normalizedText;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function stripProviderLogEdges(text) {
|
|
108
|
+
const lines = normalizeText(text).split('\n');
|
|
109
|
+
|
|
110
|
+
while (lines.length > 0 && (PROVIDER_LOG_LINE.test(lines[0]) || lines[0].trim() === '')) {
|
|
111
|
+
lines.shift();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
while (lines.length > 0 && (PROVIDER_LOG_LINE.test(lines[lines.length - 1]) || lines[lines.length - 1].trim() === '')) {
|
|
115
|
+
lines.pop();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return lines.join('\n').trim();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizeDraftOutput(text, sourceText = text) {
|
|
122
|
+
const value = normalizeText(text).trim();
|
|
123
|
+
if (!value) {
|
|
124
|
+
return '';
|
|
125
|
+
}
|
|
126
|
+
return /\n\s*$/.test(String(sourceText || '')) ? `${value}\n` : value;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function extractCleanProviderOutput(result, options = {}) {
|
|
130
|
+
const stdout = redactSensitiveLocalValues(result?.stdout || '', options);
|
|
131
|
+
const stderr = redactSensitiveLocalValues(result?.stderr || '', options);
|
|
132
|
+
const primary = stdout.trim() ? stdout : stderr;
|
|
133
|
+
const cleaned = stripProviderLogEdges(stripPromptEcho(primary, options.prompt || ''));
|
|
134
|
+
const cleanOutput = normalizeDraftOutput(cleaned, primary);
|
|
135
|
+
|
|
136
|
+
if (cleanOutput) {
|
|
137
|
+
return {
|
|
138
|
+
cleanOutput,
|
|
139
|
+
source: stdout.trim() ? 'stdout' : 'stderr',
|
|
140
|
+
strippedPromptEcho: primary !== stripPromptEcho(primary, options.prompt || ''),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
cleanOutput: normalizeDraftOutput(primary || [stdout, stderr].filter(Boolean).join('\n'), primary),
|
|
146
|
+
source: stdout.trim() ? 'stdout' : stderr.trim() ? 'stderr' : 'empty',
|
|
147
|
+
strippedPromptEcho: false,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function safeArtifactName(scope, now = new Date()) {
|
|
152
|
+
const slug = String(scope || 'provider-output')
|
|
153
|
+
.trim()
|
|
154
|
+
.toLowerCase()
|
|
155
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
156
|
+
.replace(/^-+|-+$/g, '') || 'provider-output';
|
|
157
|
+
const stamp = now.toISOString()
|
|
158
|
+
.replace(/\.\d{3}Z$/, 'z')
|
|
159
|
+
.replace(/[^0-9a-z]+/gi, '-')
|
|
160
|
+
.toLowerCase()
|
|
161
|
+
.replace(/^-+|-+$/g, '');
|
|
162
|
+
return `${stamp}-${slug}.json`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function toRelativePosix(root, filePath) {
|
|
166
|
+
return path.relative(root, filePath).split(path.sep).join('/');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function writeRawProviderArtifact(projectRoot, runId, scope, result, options = {}) {
|
|
170
|
+
if (!runId) {
|
|
171
|
+
throw new Error(formatError('missing AI run id for raw provider artifact'));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const now = options.now || new Date();
|
|
175
|
+
const rawDir = path.join(quiverInternalPaths(projectRoot).runsDir, String(runId), 'raw');
|
|
176
|
+
const rawPath = path.join(rawDir, safeArtifactName(scope, now));
|
|
177
|
+
const serializedError = result?.error
|
|
178
|
+
? {
|
|
179
|
+
code: result.error.code || null,
|
|
180
|
+
message: result.error.message || String(result.error),
|
|
181
|
+
provider: result.error.provider || null,
|
|
182
|
+
command: result.error.command || null,
|
|
183
|
+
}
|
|
184
|
+
: null;
|
|
185
|
+
const artifact = {
|
|
186
|
+
schema_version: RAW_ARTIFACT_SCHEMA_VERSION,
|
|
187
|
+
kind: 'provider-output',
|
|
188
|
+
scope: String(scope || 'provider-output'),
|
|
189
|
+
created_at: now.toISOString(),
|
|
190
|
+
provider: result?.provider || null,
|
|
191
|
+
command: result?.command || null,
|
|
192
|
+
args: Array.isArray(result?.args) ? result.args.slice() : [],
|
|
193
|
+
cwd: result?.cwd ? redactSensitiveLocalValues(result.cwd, { projectRoot }) : null,
|
|
194
|
+
ok: Boolean(result?.ok),
|
|
195
|
+
dry_run: Boolean(result?.dryRun),
|
|
196
|
+
exit_code: typeof result?.exitCode === 'number' ? result.exitCode : null,
|
|
197
|
+
signal: result?.signal || null,
|
|
198
|
+
timeout_ms: typeof result?.timeoutMs === 'number' ? result.timeoutMs : null,
|
|
199
|
+
prompt_transport: result?.promptTransport || null,
|
|
200
|
+
stdout: redactSensitiveLocalValues(result?.stdout || '', { projectRoot }),
|
|
201
|
+
stderr: redactSensitiveLocalValues(result?.stderr || '', { projectRoot }),
|
|
202
|
+
error: serializedError ? JSON.parse(redactSensitiveLocalValues(JSON.stringify(serializedError), { projectRoot })) : null,
|
|
203
|
+
metadata: options.metadata || {},
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
fs.mkdirSync(rawDir, { recursive: true });
|
|
207
|
+
fs.writeFileSync(rawPath, `${JSON.stringify(artifact, null, 2)}\n`);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
filePath: rawPath,
|
|
211
|
+
path: toRelativePosix(projectRoot, rawPath),
|
|
212
|
+
artifact,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function compactTextToByteLimit(text, maxBytes) {
|
|
217
|
+
let value = normalizeText(text).trim();
|
|
218
|
+
if (byteLength(value) <= maxBytes) {
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
while (byteLength(value) > maxBytes && value.length > 0) {
|
|
223
|
+
value = value.slice(0, Math.max(0, value.length - Math.ceil((byteLength(value) - maxBytes) / 2) - 32)).trimEnd();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function compactRevisionInput(inputText, options = {}) {
|
|
230
|
+
const limits = resolveAiArtifactLimits(options);
|
|
231
|
+
const originalText = normalizeText(inputText);
|
|
232
|
+
const originalBytes = byteLength(originalText);
|
|
233
|
+
|
|
234
|
+
if (originalBytes <= limits.maxRevisionInputBytes) {
|
|
235
|
+
return {
|
|
236
|
+
text: originalText,
|
|
237
|
+
compaction: null,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const lines = originalText.split('\n');
|
|
242
|
+
const selected = [];
|
|
243
|
+
const seen = new Set();
|
|
244
|
+
const addLine = (line) => {
|
|
245
|
+
const key = line;
|
|
246
|
+
if (seen.has(key)) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
seen.add(key);
|
|
250
|
+
selected.push(line);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
lines.slice(0, 24).forEach(addLine);
|
|
254
|
+
for (const line of lines) {
|
|
255
|
+
if (/^\s*#{1,6}\s+/.test(line) || IMPORTANT_REVISION_LINE.test(line)) {
|
|
256
|
+
addLine(line);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
lines.slice(-24).forEach(addLine);
|
|
260
|
+
|
|
261
|
+
const preface = [
|
|
262
|
+
`[Quiver compacted oversized revise input from ${originalBytes} bytes before provider execution.]`,
|
|
263
|
+
'[Preserved headings and lines mentioning decisions, risks, files, acceptance criteria, validation, blockers, dependencies, assumptions, pending work, rollback, and evidence.]',
|
|
264
|
+
'',
|
|
265
|
+
].join('\n');
|
|
266
|
+
const compacted = `${preface}${selected.join('\n')}`;
|
|
267
|
+
const targetBytes = Math.min(limits.compactedRevisionInputBytes, limits.maxRevisionInputBytes);
|
|
268
|
+
const finalText = compactTextToByteLimit(compacted, targetBytes);
|
|
269
|
+
const compactedBytes = byteLength(finalText);
|
|
270
|
+
|
|
271
|
+
if (compactedBytes > limits.maxRevisionInputBytes) {
|
|
272
|
+
const error = new Error(formatError(`ai revise input is too large after compaction (${compactedBytes} bytes; limit ${limits.maxRevisionInputBytes}). Reduce feedback size and retry.`));
|
|
273
|
+
error.code = 'AI_INPUT_TOO_LARGE';
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
text: `${finalText.trimEnd()}\n`,
|
|
279
|
+
compaction: {
|
|
280
|
+
compacted: true,
|
|
281
|
+
original_bytes: originalBytes,
|
|
282
|
+
compacted_bytes: compactedBytes,
|
|
283
|
+
max_revision_input_bytes: limits.maxRevisionInputBytes,
|
|
284
|
+
preserved: ['headings', 'decisions', 'risks', 'files', 'acceptance criteria', 'validation', 'blockers', 'dependencies', 'assumptions', 'rollback', 'evidence'],
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function assertProviderPromptWithinLimit(prompt, options = {}) {
|
|
290
|
+
const limits = resolveAiArtifactLimits(options);
|
|
291
|
+
const promptBytes = byteLength(prompt);
|
|
292
|
+
|
|
293
|
+
if (promptBytes <= limits.maxProviderPromptBytes) {
|
|
294
|
+
return {
|
|
295
|
+
prompt,
|
|
296
|
+
bytes: promptBytes,
|
|
297
|
+
maxProviderPromptBytes: limits.maxProviderPromptBytes,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const error = new Error(formatError(`provider prompt is too large (${promptBytes} bytes; limit ${limits.maxProviderPromptBytes}). Reduce the input, split the work, or run ai revise with focused feedback before invoking the provider.`));
|
|
302
|
+
error.code = 'AI_PROMPT_TOO_LARGE';
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
module.exports = {
|
|
307
|
+
DEFAULT_COMPACTED_REVISION_INPUT_BYTES,
|
|
308
|
+
DEFAULT_MAX_PROVIDER_PROMPT_BYTES,
|
|
309
|
+
DEFAULT_MAX_REVISION_INPUT_BYTES,
|
|
310
|
+
RAW_ARTIFACT_SCHEMA_VERSION,
|
|
311
|
+
assertProviderPromptWithinLimit,
|
|
312
|
+
byteLength,
|
|
313
|
+
compactRevisionInput,
|
|
314
|
+
extractCleanProviderOutput,
|
|
315
|
+
redactSensitiveLocalValues,
|
|
316
|
+
resolveAiArtifactLimits,
|
|
317
|
+
writeRawProviderArtifact,
|
|
318
|
+
};
|
|
@@ -45,9 +45,13 @@ const DEFAULT_CONTEXT_PACK_BY_ROLE = Object.freeze({
|
|
|
45
45
|
|
|
46
46
|
const PACK_ORDER = ['full', 'planning', 'slice', 'minimal'];
|
|
47
47
|
const CONTEXT_PREPARED_DOC_PATHS = Object.freeze([
|
|
48
|
+
'docs/INDEX.md',
|
|
49
|
+
'docs/PROJECT_MAP.md',
|
|
48
50
|
'docs/AI_CONTEXT.md',
|
|
49
51
|
'docs/AI_ONBOARDING_PROMPT.md',
|
|
50
52
|
'docs/CONTEXTO.md',
|
|
53
|
+
'docs/WORKFLOW.md',
|
|
54
|
+
'docs/ARCHITECTURE.md',
|
|
51
55
|
'docs/STATUS.md',
|
|
52
56
|
'docs/DECISIONS.md',
|
|
53
57
|
]);
|
|
@@ -3,6 +3,7 @@ const path = require('node:path');
|
|
|
3
3
|
|
|
4
4
|
const { resolveProfileProvider } = require('../agent-profiles');
|
|
5
5
|
const { branchDelete, runGit, statusPorcelain, worktreeAdd, worktreePrune, worktreeRemove } = require('../git');
|
|
6
|
+
const { withLock } = require('../locks');
|
|
6
7
|
const { safeBranchName, worktreesRootForRepo } = require('../slice');
|
|
7
8
|
const { buildGraph, computeLevels, detectFileConflicts, isFoundationSliceId, readAllSlices, topoSort, SliceGraphError } = require('../slice-graph');
|
|
8
9
|
const { runExecuteSlice } = require('./executor');
|
|
@@ -35,6 +36,9 @@ function summarizeSlice(node, repoRoot) {
|
|
|
35
36
|
title: node.title || node.sliceId,
|
|
36
37
|
status: node.status || 'draft',
|
|
37
38
|
files: Array.isArray(node.files) ? node.files : [],
|
|
39
|
+
expected_read_paths: Array.isArray(node.expected_read_paths) ? node.expected_read_paths : [],
|
|
40
|
+
allowed_write_paths: Array.isArray(node.allowed_write_paths) ? node.allowed_write_paths : [],
|
|
41
|
+
validation_hints: Array.isArray(node.validation_hints) ? node.validation_hints : [],
|
|
38
42
|
depends_on: Array.isArray(node.depends_on) ? node.depends_on : [],
|
|
39
43
|
parallel_safe: node.parallel_safe || null,
|
|
40
44
|
parallel_safe_reason: node.parallel_safe_reason || null,
|
|
@@ -274,7 +278,7 @@ function formatHumanExecutionPlan(report) {
|
|
|
274
278
|
|
|
275
279
|
for (const level of report.ready_levels) {
|
|
276
280
|
const modeLabel = level.parallel_ready ? 'parallel-ready' : 'sequential';
|
|
277
|
-
lines.push(`
|
|
281
|
+
lines.push(`Wave ${level.index} (${modeLabel})`);
|
|
278
282
|
lines.push(`Worktree strategy: ${level.worktree_strategy.mode}`);
|
|
279
283
|
if (level.fallback_reason) {
|
|
280
284
|
lines.push(`Fallback: ${level.fallback_reason}`);
|
|
@@ -282,6 +286,7 @@ function formatHumanExecutionPlan(report) {
|
|
|
282
286
|
|
|
283
287
|
for (const slice of level.slices) {
|
|
284
288
|
lines.push(`- ${slice.ref} [${slice.status}]`);
|
|
289
|
+
lines.push(` parallel_safe: ${slice.parallel_safe || 'unspecified'}${slice.parallel_safe_reason ? ` (${slice.parallel_safe_reason})` : ''}`);
|
|
285
290
|
}
|
|
286
291
|
|
|
287
292
|
if (level.conflicts.length > 0) {
|
|
@@ -454,6 +459,7 @@ async function runSequentialGroup(repoRoot, level, group, options = {}) {
|
|
|
454
459
|
providerExplicit: options.providerExplicit,
|
|
455
460
|
role: options.role,
|
|
456
461
|
slice: slice.slice_path,
|
|
462
|
+
skipWorktreeBranchCheck: true,
|
|
457
463
|
timeout: options.timeout,
|
|
458
464
|
});
|
|
459
465
|
results.push({
|
|
@@ -481,6 +487,13 @@ async function runParallelGroupInWorktrees(repoRoot, level, group, options = {})
|
|
|
481
487
|
const slices = group.slice_refs.map((ref) => level.slices.find((item) => item.ref === ref));
|
|
482
488
|
const workspaces = slices.map((slice, index) => buildDelegatedWorkspace(repoRoot, slice, runId, index, options));
|
|
483
489
|
|
|
490
|
+
return withLock(repoRoot, `execute-plan-${runId}`, {
|
|
491
|
+
command: 'ai execute-plan',
|
|
492
|
+
metadata: {
|
|
493
|
+
mode: 'delegated',
|
|
494
|
+
slices: group.slice_refs,
|
|
495
|
+
},
|
|
496
|
+
}, async () => {
|
|
484
497
|
let runResults;
|
|
485
498
|
try {
|
|
486
499
|
worktreePrune(repoRoot);
|
|
@@ -498,6 +511,7 @@ async function runParallelGroupInWorktrees(repoRoot, level, group, options = {})
|
|
|
498
511
|
providerExplicit: options.providerExplicit,
|
|
499
512
|
role: options.role,
|
|
500
513
|
slice: workspace.slice.slice_path,
|
|
514
|
+
skipWorktreeBranchCheck: true,
|
|
501
515
|
timeout: options.timeout,
|
|
502
516
|
});
|
|
503
517
|
const commit = runGit(['rev-parse', 'HEAD'], workspace.worktreePath);
|
|
@@ -536,6 +550,7 @@ async function runParallelGroupInWorktrees(repoRoot, level, group, options = {})
|
|
|
536
550
|
workspace: item.workspace.worktreePath,
|
|
537
551
|
integratedCommit: item.commit,
|
|
538
552
|
}));
|
|
553
|
+
});
|
|
539
554
|
}
|
|
540
555
|
|
|
541
556
|
async function runExecutePlan(repoRoot, options = {}) {
|