create-quiver 0.6.0 → 0.8.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/.claude/settings.local.json +45 -0
- package/.github/workflows/ci.yml +9 -32
- package/AGENTS.md.template +41 -0
- package/BACKLOG.md +139 -0
- package/CHANGELOG.md +17 -0
- package/README.md +68 -14
- package/README_FOR_AI.md +48 -16
- package/ROADMAP.md +100 -0
- package/docs/AI_CONTEXT.md.template +19 -26
- package/docs/AI_ONBOARDING_PROMPT.md.template +16 -0
- package/docs/COMMANDS.md.template +25 -0
- package/docs/CONTEXTO.md.template +4 -17
- package/docs/DECISIONS.md.template +18 -0
- package/docs/DEEP.md.template +34 -0
- package/docs/DOCUMENTATION_GUIDE.md.template +9 -7
- package/docs/GITFLOW_PR_GUIDE.md.template +7 -0
- package/docs/INDEX.md.template +11 -0
- package/docs/QUICK.md.template +27 -0
- package/docs/STANDARD.md.template +49 -0
- package/docs/STATUS.md.template +2 -2
- package/docs/SUPPORT_MATRIX.md.template +16 -4
- package/docs/TESTING_GUIDE_FOR_AI.md.template +4 -3
- package/docs/TROUBLESHOOTING.md.template +14 -0
- package/docs/WORKFLOW.md.template +21 -4
- package/docs/examples/graph.md.template +62 -0
- package/docs/examples/next.md.template +27 -0
- package/docs/examples/plan.md.template +28 -0
- package/package.json +6 -2
- package/package.template.json +16 -0
- package/scripts/check-slice-readiness.sh +6 -4
- package/scripts/cleanup-slice.sh +2 -172
- package/scripts/init-docs.sh +147 -26
- package/scripts/package-quiver.sh +5 -0
- package/scripts/start-slice.sh +3 -425
- package/specs/[project-name]/EVIDENCE_REPORT.md.template +3 -1
- package/specs/[project-name]/HANDOFF.md.template +37 -0
- package/specs/[project-name]/slices/slice-template/slice.json +7 -2
- package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-01-project-scan-command/slice.json +1 -1
- package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-02-ai-onboarding-prompt/slice.json +1 -1
- package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-03-doctor-readme-adoption-flow/slice.json +1 -1
- package/specs/quiver-v12-cross-platform-native-runtime/EVIDENCE_REPORT.md +30 -0
- package/specs/quiver-v12-cross-platform-native-runtime/SPEC.md +86 -0
- package/specs/quiver-v12-cross-platform-native-runtime/STATUS.md +29 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-01-cross-platform-support-contract/slice.json +69 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-02-node-init-docs-runtime/slice.json +76 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-03-node-migrate-analyze-doctor-flow/slice.json +74 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-04-node-slice-lifecycle-commands/slice.json +81 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-05-generated-project-scripts-and-migration/slice.json +78 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-06-cross-platform-ci-release-readiness/slice.json +74 -0
- package/specs/quiver-v13-token-efficient-ai-context/EVIDENCE_REPORT.md +28 -0
- package/specs/quiver-v13-token-efficient-ai-context/SPEC.md +68 -0
- package/specs/quiver-v13-token-efficient-ai-context/STATUS.md +26 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-01-token-efficient-ai-modes-guidance/slice.json +65 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-02-decision-log-context-checkpoint/slice.json +64 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-03-project-map-reading-order/slice.json +66 -0
- package/specs/quiver-v14-tiered-context-pack/EVIDENCE_REPORT.md +42 -0
- package/specs/quiver-v14-tiered-context-pack/SPEC.md +116 -0
- package/specs/quiver-v14-tiered-context-pack/STATUS.md +35 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-01-tiered-context-pack/slice.json +77 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-02-agents-md-router/slice.json +74 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-03-active-slice-lifecycle/slice.json +74 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-04-dedup-frontmatter/slice.json +83 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-05-doctor-smokes-tiered-pack/slice.json +84 -0
- package/specs/quiver-v15-init-required-before-migrate/EVIDENCE_REPORT.md +26 -0
- package/specs/quiver-v15-init-required-before-migrate/SPEC.md +66 -0
- package/specs/quiver-v15-init-required-before-migrate/STATUS.md +26 -0
- package/specs/quiver-v15-init-required-before-migrate/slices/slice-01-migrate-initialization-precondition/slice.json +65 -0
- package/specs/quiver-v15-init-required-before-migrate/slices/slice-02-doctor-not-initialized-guidance/slice.json +61 -0
- package/specs/quiver-v15-init-required-before-migrate/slices/slice-03-docs-smokes-init-before-migrate/slice.json +64 -0
- package/specs/quiver-v16-handoff-contract/EVIDENCE_REPORT.md +26 -0
- package/specs/quiver-v16-handoff-contract/SPEC.md +68 -0
- package/specs/quiver-v16-handoff-contract/STATUS.md +26 -0
- package/specs/quiver-v16-handoff-contract/slices/slice-01-handoff-template-and-contract/slice.json +66 -0
- package/specs/quiver-v16-handoff-contract/slices/slice-02-check-handoff-command/slice.json +70 -0
- package/specs/quiver-v16-handoff-contract/slices/slice-03-handoff-scaffold-optional/slice.json +67 -0
- package/specs/quiver-v17-orchestration-foundation/EVIDENCE_REPORT.md +32 -0
- package/specs/quiver-v17-orchestration-foundation/SPEC.md +79 -0
- package/specs/quiver-v17-orchestration-foundation/STATUS.md +31 -0
- package/specs/quiver-v17-orchestration-foundation/slices/slice-01-ci-matrix-verified/slice.json +68 -0
- package/specs/quiver-v17-orchestration-foundation/slices/slice-02-slice-graph-library/slice.json +65 -0
- package/specs/quiver-v17-orchestration-foundation/slices/slice-03-depends-on-validation/slice.json +72 -0
- package/specs/quiver-v18-slice-orchestration/EVIDENCE_REPORT.md +38 -0
- package/specs/quiver-v18-slice-orchestration/SPEC.md +91 -0
- package/specs/quiver-v18-slice-orchestration/STATUS.md +33 -0
- package/specs/quiver-v18-slice-orchestration/slices/slice-01-plan-command/slice.json +79 -0
- package/specs/quiver-v18-slice-orchestration/slices/slice-02-graph-mvp-tree/slice.json +75 -0
- package/specs/quiver-v18-slice-orchestration/slices/slice-03-graph-extended-formats/slice.json +70 -0
- package/specs/quiver-v18-slice-orchestration/slices/slice-04-next-command/slice.json +73 -0
- package/specs/quiver-v18-stabilization/EVIDENCE_REPORT.md +26 -0
- package/specs/quiver-v18-stabilization/SPEC.md +62 -0
- package/specs/quiver-v18-stabilization/STATUS.md +30 -0
- package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/CLOSURE_BRIEF.md +29 -0
- package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/EXECUTION_BRIEF.md +134 -0
- package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/slice.json +56 -0
- package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/CLOSURE_BRIEF.md +29 -0
- package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/EXECUTION_BRIEF.md +118 -0
- package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/slice.json +57 -0
- package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/CLOSURE_BRIEF.md +23 -0
- package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/EXECUTION_BRIEF.md +73 -0
- package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/slice.json +49 -0
- package/src/create-quiver/commands/graph.js +97 -0
- package/src/create-quiver/commands/next.js +134 -0
- package/src/create-quiver/commands/plan.js +205 -0
- package/src/create-quiver/index.js +476 -123
- package/src/create-quiver/lib/analyze.js +9 -0
- package/src/create-quiver/lib/doctor.js +212 -0
- package/src/create-quiver/lib/git.js +154 -0
- package/src/create-quiver/lib/handoff.js +104 -0
- package/src/create-quiver/lib/init-docs.js +674 -0
- package/src/create-quiver/lib/json.js +14 -0
- package/src/create-quiver/lib/lifecycle.js +479 -0
- package/src/create-quiver/lib/paths.js +19 -0
- package/src/create-quiver/lib/readiness.js +354 -0
- package/src/create-quiver/lib/renderers/dot.js +129 -0
- package/src/create-quiver/lib/renderers/mermaid.js +119 -0
- package/src/create-quiver/lib/renderers/tree.js +116 -0
- package/src/create-quiver/lib/scope.js +5 -0
- package/src/create-quiver/lib/slice-graph.js +453 -0
- package/src/create-quiver/lib/slice.js +195 -0
- package/src/create-quiver/lib/state.js +139 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { relativePosixPath } = require('../lib/paths');
|
|
4
|
+
const { buildGraph, readAllSlices, topoSort } = require('../lib/slice-graph');
|
|
5
|
+
|
|
6
|
+
const EXCLUDED_STATUSES = new Set(['completed', 'skipped', 'cancelled']);
|
|
7
|
+
|
|
8
|
+
function toHourCount(value) {
|
|
9
|
+
const parsed = Number(value);
|
|
10
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function compareRefs(left, right) {
|
|
14
|
+
return String(left || '').localeCompare(String(right || ''));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function sliceToPlanItem(repoRoot, slice, index, readySet) {
|
|
18
|
+
return {
|
|
19
|
+
index,
|
|
20
|
+
ref: slice.ref,
|
|
21
|
+
spec_family: slice.specFamily || '',
|
|
22
|
+
spec_slug: slice.specSlug,
|
|
23
|
+
slice_id: slice.sliceId,
|
|
24
|
+
slice_path: relativePosixPath(repoRoot, slice.slicePath),
|
|
25
|
+
ticket: slice.ticket || '',
|
|
26
|
+
title: slice.title || slice.sliceId,
|
|
27
|
+
status: slice.status || 'draft',
|
|
28
|
+
hours: toHourCount(slice.json?.estimated_hours),
|
|
29
|
+
files: Array.isArray(slice.files) ? slice.files : [],
|
|
30
|
+
depends_on: Array.isArray(slice.depends_on) ? slice.depends_on : [],
|
|
31
|
+
ready: readySet.has(slice.ref),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildSubgraph(graph, refs) {
|
|
36
|
+
const refSet = new Set(refs);
|
|
37
|
+
return {
|
|
38
|
+
nodes: graph.nodes.filter((node) => refSet.has(node.ref)),
|
|
39
|
+
edges: graph.edges.filter((edge) => refSet.has(edge.from) && refSet.has(edge.to)),
|
|
40
|
+
cycles: graph.cycles.filter((cycle) => cycle.every((ref) => refSet.has(ref))),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildReadySet(graph, pendingRefs) {
|
|
45
|
+
const pendingSet = new Set(pendingRefs);
|
|
46
|
+
const incomingCounts = new Map(pendingRefs.map((ref) => [ref, 0]));
|
|
47
|
+
|
|
48
|
+
for (const edge of graph.edges) {
|
|
49
|
+
if (!pendingSet.has(edge.from) || !pendingSet.has(edge.to)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
incomingCounts.set(edge.to, (incomingCounts.get(edge.to) || 0) + 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return new Set(Array.from(incomingCounts.entries()).filter(([, count]) => count === 0).map(([ref]) => ref));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildCriticalPath(graph, refs) {
|
|
59
|
+
if (refs.length === 0) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const subgraph = buildSubgraph(graph, refs);
|
|
64
|
+
const ordered = topoSort(subgraph);
|
|
65
|
+
const incoming = new Map(ordered.map((node) => [node.ref, []]));
|
|
66
|
+
|
|
67
|
+
for (const edge of subgraph.edges) {
|
|
68
|
+
if (!incoming.has(edge.to)) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
incoming.get(edge.to).push(edge.from);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const best = new Map();
|
|
75
|
+
|
|
76
|
+
for (const node of ordered) {
|
|
77
|
+
const hours = toHourCount(node.json?.estimated_hours);
|
|
78
|
+
const predecessors = incoming.get(node.ref) || [];
|
|
79
|
+
let candidate = {
|
|
80
|
+
hours,
|
|
81
|
+
path: [node.ref],
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
for (const pred of predecessors) {
|
|
85
|
+
const prev = best.get(pred);
|
|
86
|
+
if (!prev) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const nextCandidate = {
|
|
90
|
+
hours: prev.hours + hours,
|
|
91
|
+
path: [...prev.path, node.ref],
|
|
92
|
+
};
|
|
93
|
+
const pathLabel = nextCandidate.path.join('>');
|
|
94
|
+
const bestLabel = candidate.path.join('>');
|
|
95
|
+
if (nextCandidate.hours > candidate.hours || (nextCandidate.hours === candidate.hours && compareRefs(pathLabel, bestLabel) < 0)) {
|
|
96
|
+
candidate = nextCandidate;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
best.set(node.ref, candidate);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let winner = null;
|
|
104
|
+
for (const node of ordered) {
|
|
105
|
+
const candidate = best.get(node.ref);
|
|
106
|
+
if (!candidate) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (!winner || candidate.hours > winner.hours || (candidate.hours === winner.hours && compareRefs(candidate.path.join('>'), winner.path.join('>')) < 0)) {
|
|
110
|
+
winner = candidate;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return winner ? winner.path : [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function collectPlan(repoRoot, options = {}) {
|
|
118
|
+
const allSlices = readAllSlices(repoRoot);
|
|
119
|
+
const graph = buildGraph(allSlices);
|
|
120
|
+
const topo = topoSort(graph);
|
|
121
|
+
const excluded = EXCLUDED_STATUSES;
|
|
122
|
+
const specSlug = options.specSlug ? String(options.specSlug).trim() : '';
|
|
123
|
+
|
|
124
|
+
const pendingRefs = new Set(
|
|
125
|
+
graph.nodes
|
|
126
|
+
.filter((node) => !excluded.has(String(node.status || '').toLowerCase()))
|
|
127
|
+
.map((node) => node.ref),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const readyRefs = buildReadySet(graph, Array.from(pendingRefs));
|
|
131
|
+
|
|
132
|
+
const selectedNodes = topo.filter((node) => {
|
|
133
|
+
if (!pendingRefs.has(node.ref)) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
if (specSlug && node.specSlug !== specSlug) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const readyNodes = selectedNodes.filter((node) => readyRefs.has(node.ref));
|
|
143
|
+
const targetNodes = options.onlyReady ? readyNodes : selectedNodes;
|
|
144
|
+
const criticalPath = buildCriticalPath(graph, targetNodes.map((node) => node.ref));
|
|
145
|
+
const totalHours = targetNodes.reduce((sum, node) => sum + toHourCount(node.json?.estimated_hours), 0);
|
|
146
|
+
const plan = targetNodes.map((node, index) => sliceToPlanItem(repoRoot, node, index + 1, readyRefs));
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
critical_path: criticalPath,
|
|
150
|
+
plan,
|
|
151
|
+
total_hours: totalHours,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function formatHumanPlan(report, options = {}) {
|
|
156
|
+
const unicode = Boolean(options.unicode) || /UTF-8/i.test(process.env.LANG || '');
|
|
157
|
+
const title = options.onlyReady ? 'Ready slices' : 'Quiver plan';
|
|
158
|
+
const pathSeparator = unicode ? ' → ' : ' -> ';
|
|
159
|
+
const lines = [title, `Total hours: ${report.total_hours}`, `Critical path: ${report.critical_path.length > 0 ? report.critical_path.join(pathSeparator) : '-'}`, ''];
|
|
160
|
+
|
|
161
|
+
if (report.plan.length === 0) {
|
|
162
|
+
lines.push('No pending slices found.');
|
|
163
|
+
return `${lines.join('\n')}\n`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const rows = report.plan.map((item) => [
|
|
167
|
+
`[${item.index}]`,
|
|
168
|
+
item.ticket || '-',
|
|
169
|
+
item.ref,
|
|
170
|
+
item.title,
|
|
171
|
+
`${item.hours}`,
|
|
172
|
+
item.status,
|
|
173
|
+
]);
|
|
174
|
+
|
|
175
|
+
const widths = rows[0].map((_, colIndex) => Math.max(
|
|
176
|
+
rows.reduce((max, row) => Math.max(max, row[colIndex].length), 0),
|
|
177
|
+
['[N]', 'TICKET', 'SPEC/SLICE', 'TITLE', 'HOURS', 'STATUS'][colIndex].length,
|
|
178
|
+
));
|
|
179
|
+
|
|
180
|
+
lines.push(['[N]', 'TICKET', 'SPEC/SLICE', 'TITLE', 'HOURS', 'STATUS'].map((label, index) => label.padEnd(widths[index])).join(' '));
|
|
181
|
+
lines.push(widths.map((width) => '-'.repeat(width)).join(' '));
|
|
182
|
+
|
|
183
|
+
for (const row of rows) {
|
|
184
|
+
lines.push(row.map((value, index) => value.padEnd(widths[index])).join(' '));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return `${lines.join('\n')}\n`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function runPlan(repoRoot, options = {}) {
|
|
191
|
+
const report = collectPlan(repoRoot, options);
|
|
192
|
+
if (options.json) {
|
|
193
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
194
|
+
return report;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
process.stdout.write(formatHumanPlan(report, options));
|
|
198
|
+
return report;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = {
|
|
202
|
+
collectPlan,
|
|
203
|
+
formatHumanPlan,
|
|
204
|
+
runPlan,
|
|
205
|
+
};
|