create-quiver 0.4.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/.github/ISSUE_TEMPLATE/bug_report.md +15 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
- package/.github/pull_request_template.md +4 -0
- package/.github/workflows/ci.yml +74 -0
- package/CHANGELOG.md +24 -0
- package/CODE_OF_CONDUCT.md +12 -0
- package/CONTRIBUTING.md +15 -0
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/README_FOR_AI.md +90 -0
- package/ROADMAP.md +20 -0
- package/SECURITY.md +12 -0
- package/TEMPLATE.md +108 -0
- package/bin/create-quiver.js +8 -0
- package/docs/CONTEXTO.md.template +45 -0
- package/docs/DOCUMENTATION_GUIDE.md.template +34 -0
- package/docs/GITFLOW_PR_GUIDE.md.template +52 -0
- package/docs/INDEX.md.template +41 -0
- package/docs/MOCK_DATA_GUIDE.md.template +31 -0
- package/docs/MULTI_AGENT_WORKFLOW.md.template +31 -0
- package/docs/STATUS.md.template +26 -0
- package/docs/SUPPORT_MATRIX.md.template +31 -0
- package/docs/TESTING_GUIDE_FOR_AI.md.template +42 -0
- package/docs/TROUBLESHOOTING.md.template +70 -0
- package/docs/UI_STANDARDS.md.template +31 -0
- package/docs/WORKFLOW.md.template +56 -0
- package/docs/ai/LESSONS.md.template +24 -0
- package/docs/ai/PRINCIPLES.md +25 -0
- package/docs/ai/RULES.yaml +33 -0
- package/i18n/es/README.md +15 -0
- package/i18n/es/README_FOR_AI.md +6 -0
- package/i18n/es/TEMPLATE.md +18 -0
- package/i18n/es/docs/CONTEXTO.md.template +9 -0
- package/i18n/es/docs/DOCUMENTATION_GUIDE.md.template +4 -0
- package/i18n/es/docs/GITFLOW_PR_GUIDE.md.template +4 -0
- package/i18n/es/docs/INDEX.md.template +10 -0
- package/i18n/es/docs/MOCK_DATA_GUIDE.md.template +4 -0
- package/i18n/es/docs/MULTI_AGENT_WORKFLOW.md.template +4 -0
- package/i18n/es/docs/STATUS.md.template +9 -0
- package/i18n/es/docs/TESTING_GUIDE_FOR_AI.md.template +7 -0
- package/i18n/es/docs/UI_STANDARDS.md.template +4 -0
- package/i18n/es/docs/WORKFLOW.md.template +6 -0
- package/i18n/es/docs/ai/LESSONS.md.template +3 -0
- package/i18n/es/docs/ai/PRINCIPLES.md +7 -0
- package/i18n/es/docs/ai/RULES.yaml +7 -0
- package/package.json +19 -0
- package/package.template.json +10 -0
- package/scripts/check-pr-readiness.sh +138 -0
- package/scripts/check-scope.sh +150 -0
- package/scripts/check-slice-readiness.sh +319 -0
- package/scripts/cleanup-slice.sh +177 -0
- package/scripts/init-docs.sh +368 -0
- package/scripts/migrate-project.sh +218 -0
- package/scripts/package-quiver.sh +124 -0
- package/scripts/refresh-active-slices.sh +232 -0
- package/scripts/release-quiver.sh +77 -0
- package/scripts/start-slice.sh +429 -0
- package/specs/[project-name]/EVIDENCE_REPORT.md.template +15 -0
- package/specs/[project-name]/SPEC.md.template +39 -0
- package/specs/[project-name]/STATUS.md.template +22 -0
- package/specs/[project-name]/slices/pr.md.template +97 -0
- package/specs/[project-name]/slices/slice-template/slice.json +69 -0
- package/specs/quiver-v05-readme-adoption-contract/EVIDENCE_REPORT.md +21 -0
- package/specs/quiver-v05-readme-adoption-contract/SPEC.md +40 -0
- package/specs/quiver-v05-readme-adoption-contract/STATUS.md +24 -0
- package/specs/quiver-v05-readme-adoption-contract/slices/slice-01-readme-adoption-contract/slice.json +68 -0
- package/specs/quiver-v06-release-readiness/EVIDENCE_REPORT.md +23 -0
- package/specs/quiver-v06-release-readiness/SPEC.md +40 -0
- package/specs/quiver-v06-release-readiness/STATUS.md +24 -0
- package/specs/quiver-v06-release-readiness/slices/slice-01-first-npm-release-readiness/slice.json +71 -0
- package/src/create-quiver/index.js +329 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Quiver v0.5 - README Adoption Contract
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-04-21
|
|
4
|
+
**Status:** Completed
|
|
5
|
+
|
|
6
|
+
Slice numbering resets here: this spec starts at `slice-01` and does not continue any previous spec's numbering.
|
|
7
|
+
|
|
8
|
+
## Objective
|
|
9
|
+
|
|
10
|
+
Make the root README the adoption contract for Quiver: a new user should understand what Quiver is, install it with the canonical CLI, validate the scaffold, and start the first slice without reading historical implementation notes.
|
|
11
|
+
|
|
12
|
+
## Scope
|
|
13
|
+
|
|
14
|
+
### Included
|
|
15
|
+
|
|
16
|
+
- Reposition the README around `create-quiver` as the primary install path
|
|
17
|
+
- Keep manual `docs-template/` usage as a local development fallback
|
|
18
|
+
- Document the first slice workflow at a high level
|
|
19
|
+
- Separate user adoption guidance from maintainer release guidance
|
|
20
|
+
- Reduce long file inventories and link to the relevant generated docs instead
|
|
21
|
+
|
|
22
|
+
### Excluded
|
|
23
|
+
|
|
24
|
+
- CLI behavior changes
|
|
25
|
+
- Generated template content changes
|
|
26
|
+
- Hosted documentation
|
|
27
|
+
- Release automation changes
|
|
28
|
+
|
|
29
|
+
## Slices
|
|
30
|
+
|
|
31
|
+
| Slice | Title | Status |
|
|
32
|
+
|-------|-------|--------|
|
|
33
|
+
| 01 | README Adoption Contract | Completed |
|
|
34
|
+
|
|
35
|
+
## Definition of Done
|
|
36
|
+
|
|
37
|
+
- The first install command in the README uses `npx create-quiver`
|
|
38
|
+
- The README explains install, doctor validation, first slice workflow, manual fallback, and maintainer commands
|
|
39
|
+
- Commands shown in the README match the current CLI and package scripts
|
|
40
|
+
- The README no longer presents manual template copying as the primary path
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Quiver v0.5 Spec Status
|
|
2
|
+
|
|
3
|
+
**Spec:** quiver-v05-readme-adoption-contract
|
|
4
|
+
**Last updated:** 2026-04-21
|
|
5
|
+
|
|
6
|
+
Slice numbering is local to this spec. The first slice is `slice-01`.
|
|
7
|
+
|
|
8
|
+
## Status
|
|
9
|
+
|
|
10
|
+
| Slice | Title | Status | PR | Estimated hours | Actual hours |
|
|
11
|
+
|-------|-------|--------|----|-----------------|--------------|
|
|
12
|
+
| slice-01 | README Adoption Contract | Completed | - | 2 | 2 |
|
|
13
|
+
|
|
14
|
+
## Progress
|
|
15
|
+
|
|
16
|
+
- Completed slices: 1 / 1
|
|
17
|
+
- Estimated hours: 2
|
|
18
|
+
- Actual hours: 2
|
|
19
|
+
|
|
20
|
+
## Blockers
|
|
21
|
+
|
|
22
|
+
| Slice | Blocker | Since | Action needed |
|
|
23
|
+
|-------|---------|-------|---------------|
|
|
24
|
+
| - | - | - | - |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slice_id": "slice-01-readme-adoption-contract",
|
|
3
|
+
"ticket": "QUIVER-01",
|
|
4
|
+
"type": "docs",
|
|
5
|
+
"title": "README Adoption Contract",
|
|
6
|
+
"objective": "Rewrite the root README so it presents Quiver as a CLI-first adoption workflow rather than a manual docs-template copy.",
|
|
7
|
+
"description": "The existing README mixes the historical manual template flow with the newer create-quiver installer. This slice makes the README a concise adoption contract for users and maintainers without changing runtime behavior.",
|
|
8
|
+
"git": {
|
|
9
|
+
"branch_type": "docs",
|
|
10
|
+
"base_branch": "develop",
|
|
11
|
+
"branch_slug": "readme-adoption-contract",
|
|
12
|
+
"branch_name": "docs/QUIVER-01-readme-adoption-contract"
|
|
13
|
+
},
|
|
14
|
+
"must": [
|
|
15
|
+
"Make `npx create-quiver` the primary quick-start path",
|
|
16
|
+
"Document `create-quiver doctor` immediately after installation",
|
|
17
|
+
"Explain the first slice workflow at a high level",
|
|
18
|
+
"Move release guidance into a maintainer-focused section",
|
|
19
|
+
"Keep manual `docs-template/` use as a fallback or local development path",
|
|
20
|
+
"Avoid long duplicated file inventories in the root README"
|
|
21
|
+
],
|
|
22
|
+
"not_included": [
|
|
23
|
+
"Changes to CLI behavior",
|
|
24
|
+
"Changes to generated project templates",
|
|
25
|
+
"Hosted docs or website work"
|
|
26
|
+
],
|
|
27
|
+
"acceptance": [
|
|
28
|
+
"A new reader can install, validate, and begin slice work from the README alone",
|
|
29
|
+
"The README's commands match the current CLI help and package scripts",
|
|
30
|
+
"Manual copy instructions no longer appear before the CLI quick start",
|
|
31
|
+
"Maintainer release instructions are separated from user onboarding",
|
|
32
|
+
"The README links to AI, support, and troubleshooting docs instead of duplicating their full contents",
|
|
33
|
+
"The change is limited to README and spec traceability files"
|
|
34
|
+
],
|
|
35
|
+
"files": [
|
|
36
|
+
"README.md",
|
|
37
|
+
"specs/quiver-v05-readme-adoption-contract/SPEC.md",
|
|
38
|
+
"specs/quiver-v05-readme-adoption-contract/STATUS.md",
|
|
39
|
+
"specs/quiver-v05-readme-adoption-contract/EVIDENCE_REPORT.md",
|
|
40
|
+
"specs/quiver-v05-readme-adoption-contract/slices/slice-01-readme-adoption-contract/slice.json"
|
|
41
|
+
],
|
|
42
|
+
"tests": [
|
|
43
|
+
"CLI help command check",
|
|
44
|
+
"release helper help command check",
|
|
45
|
+
"package.json script existence check",
|
|
46
|
+
"README target link existence check",
|
|
47
|
+
"git diff whitespace check"
|
|
48
|
+
],
|
|
49
|
+
"documentation": [
|
|
50
|
+
"specs/quiver-v05-readme-adoption-contract/SPEC.md",
|
|
51
|
+
"specs/quiver-v05-readme-adoption-contract/STATUS.md",
|
|
52
|
+
"specs/quiver-v05-readme-adoption-contract/EVIDENCE_REPORT.md"
|
|
53
|
+
],
|
|
54
|
+
"dependencies": [
|
|
55
|
+
"quiver-v04-zero-friction-installation"
|
|
56
|
+
],
|
|
57
|
+
"assumptions": [
|
|
58
|
+
"The README should stay concise and point to detailed generated docs",
|
|
59
|
+
"The canonical install path is the npm CLI entrypoint"
|
|
60
|
+
],
|
|
61
|
+
"estimated_hours": 2,
|
|
62
|
+
"actual_hours": 2,
|
|
63
|
+
"status": "completed",
|
|
64
|
+
"blocked_reason": null,
|
|
65
|
+
"ready_at": "2026-04-21T00:00:00Z",
|
|
66
|
+
"started_at": "2026-04-21T00:00:00Z",
|
|
67
|
+
"completed_at": "2026-04-21T00:00:00Z"
|
|
68
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Quiver v0.6 Evidence Report
|
|
2
|
+
|
|
3
|
+
**Spec:** quiver-v06-release-readiness
|
|
4
|
+
**Last updated:** 2026-04-21
|
|
5
|
+
**Status:** Completed
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
| Slice | Acceptance criteria | Status | Evidence |
|
|
10
|
+
|-------|---------------------|--------|----------|
|
|
11
|
+
| slice-01 | 6 | Completed | Release helper now supports `--publish-current`, changelog has `0.4.0`, and README documents the exact preflight and publish steps |
|
|
12
|
+
|
|
13
|
+
## Evidence by Slice
|
|
14
|
+
|
|
15
|
+
- `bash -n scripts/release-quiver.sh`
|
|
16
|
+
- `git diff --check`
|
|
17
|
+
- `node -e "const fs=require('fs'); JSON.parse(fs.readFileSync('specs/quiver-v06-release-readiness/slices/slice-01-first-npm-release-readiness/slice.json','utf8'))"`
|
|
18
|
+
|
|
19
|
+
## Preflight Notes
|
|
20
|
+
|
|
21
|
+
- `npm whoami` failed with `ENEEDAUTH`; npm login is required before publish.
|
|
22
|
+
- `npm view create-quiver version` failed with DNS/network resolution against `registry.npmjs.org`; registry reachability must be verified before publish.
|
|
23
|
+
- Current package metadata is `create-quiver@0.4.0`.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Quiver v0.6 - Release Readiness
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-04-21
|
|
4
|
+
**Status:** Completed
|
|
5
|
+
|
|
6
|
+
Slice numbering resets here: this spec starts at `slice-01` and does not continue any previous spec's numbering.
|
|
7
|
+
|
|
8
|
+
## Objective
|
|
9
|
+
|
|
10
|
+
Prepare Quiver for its first public npm release without accidentally bumping past the intended initial version.
|
|
11
|
+
|
|
12
|
+
## Scope
|
|
13
|
+
|
|
14
|
+
### Included
|
|
15
|
+
|
|
16
|
+
- Align the release helper with first-release needs
|
|
17
|
+
- Update the changelog from `Unreleased` to the intended release version
|
|
18
|
+
- Validate the local package, installer smoke, release dry run, and npm prereqs
|
|
19
|
+
- Document the publish steps and post-release verification
|
|
20
|
+
|
|
21
|
+
### Excluded
|
|
22
|
+
|
|
23
|
+
- Publishing the package during spec preparation
|
|
24
|
+
- Changing CLI behavior
|
|
25
|
+
- Changing generated project templates
|
|
26
|
+
- Hosted documentation or marketing work
|
|
27
|
+
|
|
28
|
+
## Slices
|
|
29
|
+
|
|
30
|
+
| Slice | Title | Status |
|
|
31
|
+
|-------|-------|--------|
|
|
32
|
+
| 01 | First npm Release Readiness | Completed |
|
|
33
|
+
|
|
34
|
+
## Definition of Done
|
|
35
|
+
|
|
36
|
+
- The release helper can validate and publish the current package version without forcing an unintended patch bump
|
|
37
|
+
- `CHANGELOG.md` has a `0.4.0` release entry dated 2026-04-21 and a fresh `Unreleased` section
|
|
38
|
+
- npm auth and package-name availability are checked before publish
|
|
39
|
+
- Local package, installer, and release dry-run validations pass
|
|
40
|
+
- The publish command is explicit and not run as part of this slice unless requested separately
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Quiver v0.6 Spec Status
|
|
2
|
+
|
|
3
|
+
**Spec:** quiver-v06-release-readiness
|
|
4
|
+
**Last updated:** 2026-04-21
|
|
5
|
+
|
|
6
|
+
Slice numbering is local to this spec. The first slice is `slice-01`.
|
|
7
|
+
|
|
8
|
+
## Status
|
|
9
|
+
|
|
10
|
+
| Slice | Title | Status | PR | Estimated hours | Actual hours |
|
|
11
|
+
|-------|-------|--------|----|-----------------|--------------|
|
|
12
|
+
| slice-01 | First npm Release Readiness | Completed | - | 2 | 2 |
|
|
13
|
+
|
|
14
|
+
## Progress
|
|
15
|
+
|
|
16
|
+
- Completed slices: 1 / 1
|
|
17
|
+
- Estimated hours: 2
|
|
18
|
+
- Actual hours: 2
|
|
19
|
+
|
|
20
|
+
## Blockers
|
|
21
|
+
|
|
22
|
+
| Slice | Blocker | Since | Action needed |
|
|
23
|
+
|-------|---------|-------|---------------|
|
|
24
|
+
| - | - | - | - |
|
package/specs/quiver-v06-release-readiness/slices/slice-01-first-npm-release-readiness/slice.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slice_id": "slice-01-first-npm-release-readiness",
|
|
3
|
+
"ticket": "QUIVER-01",
|
|
4
|
+
"type": "release",
|
|
5
|
+
"title": "First npm Release Readiness",
|
|
6
|
+
"objective": "Prepare the repository for publishing the first public `create-quiver` npm package at the intended current version.",
|
|
7
|
+
"description": "The package is functionally ready, but the current release helper always runs `npm version patch` when publishing. That would publish `0.4.1` as the first public release instead of the current `0.4.0`. This slice updates the release contract, changelog, and validation path so publishing is explicit and version-safe.",
|
|
8
|
+
"git": {
|
|
9
|
+
"branch_type": "release",
|
|
10
|
+
"base_branch": "develop",
|
|
11
|
+
"branch_slug": "first-npm-release-readiness",
|
|
12
|
+
"branch_name": "release/QUIVER-01-first-npm-release-readiness"
|
|
13
|
+
},
|
|
14
|
+
"must": [
|
|
15
|
+
"Update `scripts/release-quiver.sh` so maintainers can publish the current version intentionally without forcing a patch bump",
|
|
16
|
+
"Update `CHANGELOG.md` so the current release notes move from `Unreleased` to `0.4.0 - 2026-04-21`",
|
|
17
|
+
"Keep `Unreleased` available for future changes",
|
|
18
|
+
"Document the exact preflight checks for npm auth, package-name availability, package smoke, installer smoke, and release dry run",
|
|
19
|
+
"Run local validations but do not publish unless a separate explicit release request is made"
|
|
20
|
+
],
|
|
21
|
+
"not_included": [
|
|
22
|
+
"Publishing to npm",
|
|
23
|
+
"Changing package name or CLI behavior",
|
|
24
|
+
"Creating hosted docs or release marketing",
|
|
25
|
+
"Changing generated project templates"
|
|
26
|
+
],
|
|
27
|
+
"acceptance": [
|
|
28
|
+
"Maintainers can publish `create-quiver@0.4.0` without an unintended version bump",
|
|
29
|
+
"`CHANGELOG.md` has a dated `0.4.0` section and a fresh `Unreleased` section",
|
|
30
|
+
"Release dry run still validates installer and package smoke checks",
|
|
31
|
+
"The spec records npm auth and registry reachability as required preflight gates",
|
|
32
|
+
"The publish step remains explicit and separate from this preparation work",
|
|
33
|
+
"No npm publish command is executed during this slice"
|
|
34
|
+
],
|
|
35
|
+
"files": [
|
|
36
|
+
"scripts/release-quiver.sh",
|
|
37
|
+
"CHANGELOG.md",
|
|
38
|
+
"README.md",
|
|
39
|
+
"specs/quiver-v06-release-readiness/SPEC.md",
|
|
40
|
+
"specs/quiver-v06-release-readiness/STATUS.md",
|
|
41
|
+
"specs/quiver-v06-release-readiness/EVIDENCE_REPORT.md",
|
|
42
|
+
"specs/quiver-v06-release-readiness/slices/slice-01-first-npm-release-readiness/slice.json"
|
|
43
|
+
],
|
|
44
|
+
"tests": [
|
|
45
|
+
"npm auth and package-name preflight are documented in the README",
|
|
46
|
+
"package smoke",
|
|
47
|
+
"installer smoke",
|
|
48
|
+
"release dry run",
|
|
49
|
+
"git diff whitespace check"
|
|
50
|
+
],
|
|
51
|
+
"documentation": [
|
|
52
|
+
"specs/quiver-v06-release-readiness/SPEC.md",
|
|
53
|
+
"specs/quiver-v06-release-readiness/STATUS.md",
|
|
54
|
+
"specs/quiver-v06-release-readiness/EVIDENCE_REPORT.md"
|
|
55
|
+
],
|
|
56
|
+
"dependencies": [
|
|
57
|
+
"quiver-v05-readme-adoption-contract"
|
|
58
|
+
],
|
|
59
|
+
"assumptions": [
|
|
60
|
+
"The first public release should use the current package version `0.4.0`",
|
|
61
|
+
"The package name remains `create-quiver` unless npm availability proves otherwise",
|
|
62
|
+
"Publishing requires separate explicit approval after this readiness slice is merged"
|
|
63
|
+
],
|
|
64
|
+
"estimated_hours": 2,
|
|
65
|
+
"actual_hours": 2,
|
|
66
|
+
"status": "completed",
|
|
67
|
+
"blocked_reason": null,
|
|
68
|
+
"ready_at": "2026-04-21T00:00:00Z",
|
|
69
|
+
"started_at": "2026-04-21T00:00:00Z",
|
|
70
|
+
"completed_at": "2026-04-21T00:00:00Z"
|
|
71
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { execFileSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
function formatError(message) {
|
|
7
|
+
return `create-quiver: ${message}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function printUsage() {
|
|
11
|
+
console.log(`Usage:
|
|
12
|
+
npx create-quiver [options]
|
|
13
|
+
npx create-quiver doctor [options]
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
-n, --name <project-name> Project name to generate
|
|
17
|
+
-d, --dir <target-dir> Target directory to scaffold into or inspect
|
|
18
|
+
-y, --yes Skip prompts and use the provided inputs
|
|
19
|
+
-h, --help Show this help message
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
npx create-quiver --name "My Project"
|
|
23
|
+
npx create-quiver --name "My Project" --dir ./my-project
|
|
24
|
+
npx create-quiver doctor --dir ./my-project
|
|
25
|
+
node bin/create-quiver.js doctor --dir ./my-project
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseArgs(argv) {
|
|
30
|
+
const result = {
|
|
31
|
+
help: false,
|
|
32
|
+
force: false,
|
|
33
|
+
mode: 'init',
|
|
34
|
+
projectName: '',
|
|
35
|
+
targetDir: '.',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const args = [...argv];
|
|
39
|
+
if (args[0] === 'doctor') {
|
|
40
|
+
result.mode = 'doctor';
|
|
41
|
+
args.shift();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const positional = [];
|
|
45
|
+
|
|
46
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
47
|
+
const arg = args[index];
|
|
48
|
+
|
|
49
|
+
if (arg === '-h' || arg === '--help') {
|
|
50
|
+
result.help = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (arg === '-y' || arg === '--yes') {
|
|
55
|
+
result.force = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (arg === '--doctor') {
|
|
60
|
+
result.mode = 'doctor';
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (arg === '-n' || arg === '--name' || arg === '--project-name') {
|
|
65
|
+
const value = args[++index];
|
|
66
|
+
if (!value) {
|
|
67
|
+
throw new Error(formatError('missing value for --name'));
|
|
68
|
+
}
|
|
69
|
+
result.projectName = value;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (arg === '-d' || arg === '--dir' || arg === '--target') {
|
|
74
|
+
const value = args[++index];
|
|
75
|
+
if (!value) {
|
|
76
|
+
throw new Error(formatError('missing value for --dir'));
|
|
77
|
+
}
|
|
78
|
+
result.targetDir = value;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (arg.startsWith('-')) {
|
|
83
|
+
throw new Error(formatError(`unknown flag: ${arg}`));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
positional.push(arg);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (result.mode === 'init') {
|
|
90
|
+
if (!result.projectName && positional.length > 0) {
|
|
91
|
+
result.projectName = positional.shift();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (positional.length > 0) {
|
|
95
|
+
result.targetDir = positional.shift();
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
if (positional.length > 0) {
|
|
99
|
+
result.targetDir = positional.shift();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (positional.length > 0) {
|
|
104
|
+
throw new Error(formatError('too many positional arguments'));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function toProjectSlug(projectName) {
|
|
111
|
+
return projectName
|
|
112
|
+
.normalize('NFKD')
|
|
113
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
114
|
+
.toLowerCase()
|
|
115
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
116
|
+
.replace(/^-+|-+$/g, '') || 'quiver-project';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function runCommand(command, args, options = {}) {
|
|
120
|
+
return execFileSync(command, args, {
|
|
121
|
+
encoding: 'utf8',
|
|
122
|
+
stdio: ['ignore', 'pipe', 'inherit'],
|
|
123
|
+
...options,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function packTemplate(packageRoot, tempRoot) {
|
|
128
|
+
const packDir = path.join(tempRoot, 'pack');
|
|
129
|
+
const extractDir = path.join(tempRoot, 'extract');
|
|
130
|
+
const npmCache = path.join(tempRoot, 'npm-cache');
|
|
131
|
+
|
|
132
|
+
fs.mkdirSync(packDir, { recursive: true });
|
|
133
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
134
|
+
fs.mkdirSync(npmCache, { recursive: true });
|
|
135
|
+
|
|
136
|
+
const packOutput = runCommand('npm', ['pack', '--json', '--pack-destination', packDir], {
|
|
137
|
+
cwd: packageRoot,
|
|
138
|
+
env: {
|
|
139
|
+
...process.env,
|
|
140
|
+
npm_config_cache: npmCache,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const packInfo = JSON.parse(packOutput.trim());
|
|
145
|
+
const tarballPath = path.join(packDir, packInfo[0].filename);
|
|
146
|
+
|
|
147
|
+
if (!fs.existsSync(tarballPath)) {
|
|
148
|
+
throw new Error(formatError(`pack output not found at ${tarballPath}`));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
runCommand('tar', ['-xzf', tarballPath, '-C', extractDir]);
|
|
152
|
+
|
|
153
|
+
return path.join(extractDir, 'package');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function ensureDir(dirPath) {
|
|
157
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function copyTemplate(templateRoot, targetDir) {
|
|
161
|
+
const docsTemplateDir = path.join(targetDir, 'docs-template');
|
|
162
|
+
|
|
163
|
+
if (fs.existsSync(docsTemplateDir)) {
|
|
164
|
+
throw new Error(formatError(`docs-template already exists at ${docsTemplateDir}`));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fs.cpSync(templateRoot, docsTemplateDir, { recursive: true });
|
|
168
|
+
|
|
169
|
+
return docsTemplateDir;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function runInitDocs(repoRoot, projectName) {
|
|
173
|
+
runCommand('bash', ['docs-template/scripts/init-docs.sh', projectName], {
|
|
174
|
+
cwd: repoRoot,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function listGeneratedSpecDirs(projectRoot) {
|
|
179
|
+
const specsDir = path.join(projectRoot, 'specs');
|
|
180
|
+
|
|
181
|
+
if (!fs.existsSync(specsDir)) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return fs.readdirSync(specsDir, { withFileTypes: true })
|
|
186
|
+
.filter((entry) => entry.isDirectory())
|
|
187
|
+
.map((entry) => entry.name)
|
|
188
|
+
.filter((entry) => entry !== '[project-name]' && !entry.startsWith('quiver-'));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function assertFilesExist(root, relativePaths) {
|
|
192
|
+
return relativePaths.filter((relativePath) => !fs.existsSync(path.join(root, relativePath)));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function assertExecutablesExist(root, relativePaths) {
|
|
196
|
+
return relativePaths.filter((relativePath) => {
|
|
197
|
+
const absolutePath = path.join(root, relativePath);
|
|
198
|
+
|
|
199
|
+
if (!fs.existsSync(absolutePath)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const mode = fs.statSync(absolutePath).mode;
|
|
204
|
+
return (mode & 0o111) === 0;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function loadPackageJson(projectRoot) {
|
|
209
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
210
|
+
|
|
211
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
212
|
+
throw new Error(formatError(`missing package.json in ${projectRoot}`));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function runDoctor(targetDir) {
|
|
219
|
+
const projectRoot = path.resolve(process.cwd(), targetDir);
|
|
220
|
+
|
|
221
|
+
if (!fs.existsSync(projectRoot)) {
|
|
222
|
+
throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const generatedSpecs = listGeneratedSpecDirs(projectRoot);
|
|
226
|
+
if (generatedSpecs.length !== 1) {
|
|
227
|
+
throw new Error(formatError(`expected exactly one generated spec directory, found ${generatedSpecs.length || 0}`));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const projectSlug = generatedSpecs[0];
|
|
231
|
+
const requiredFiles = [
|
|
232
|
+
'README.md',
|
|
233
|
+
'docs/INDEX.md',
|
|
234
|
+
'docs/CONTEXTO.md',
|
|
235
|
+
'docs/WORKFLOW.md',
|
|
236
|
+
'docs/SUPPORT_MATRIX.md',
|
|
237
|
+
'docs/TROUBLESHOOTING.md',
|
|
238
|
+
'docs/TESTING_GUIDE_FOR_AI.md',
|
|
239
|
+
'docs/ai/PRINCIPLES.md',
|
|
240
|
+
'docs/ai/RULES.yaml',
|
|
241
|
+
'docs/ai/LESSONS.md',
|
|
242
|
+
`specs/${projectSlug}/SPEC.md`,
|
|
243
|
+
`specs/${projectSlug}/STATUS.md`,
|
|
244
|
+
`specs/${projectSlug}/EVIDENCE_REPORT.md`,
|
|
245
|
+
'package.json',
|
|
246
|
+
'.github/pull_request_template.md',
|
|
247
|
+
'.github/ISSUE_TEMPLATE/bug_report.md',
|
|
248
|
+
'.github/ISSUE_TEMPLATE/feature_request.md',
|
|
249
|
+
'.github/workflows/ci.yml',
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
const requiredExecutables = [
|
|
253
|
+
'tools/scripts/start-slice.sh',
|
|
254
|
+
'tools/scripts/check-slice-readiness.sh',
|
|
255
|
+
'tools/scripts/check-pr-readiness.sh',
|
|
256
|
+
'tools/scripts/cleanup-slice.sh',
|
|
257
|
+
'tools/scripts/check-scope.sh',
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
const missingFiles = assertFilesExist(projectRoot, requiredFiles);
|
|
261
|
+
const nonExecutableScripts = assertExecutablesExist(projectRoot, requiredExecutables);
|
|
262
|
+
const pkg = loadPackageJson(projectRoot);
|
|
263
|
+
const requiredScripts = ['check:slice', 'check:pr', 'start:slice', 'cleanup:slice'];
|
|
264
|
+
const missingScripts = requiredScripts.filter((name) => typeof pkg.scripts?.[name] !== 'string');
|
|
265
|
+
|
|
266
|
+
const problems = [
|
|
267
|
+
...missingFiles.map((file) => `missing file: ${file}`),
|
|
268
|
+
...nonExecutableScripts.map((file) => `missing executable bit: ${file}`),
|
|
269
|
+
...missingScripts.map((name) => `missing package.json script: ${name}`),
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
if (problems.length > 0) {
|
|
273
|
+
throw new Error(formatError(`doctor failed:\n- ${problems.join('\n- ')}`));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log(`Quiver doctor passed for ${projectRoot}`);
|
|
277
|
+
console.log(`Generated project slug: ${projectSlug}`);
|
|
278
|
+
console.log('Next steps:');
|
|
279
|
+
console.log(`- Run ${path.join(projectRoot, 'tools', 'scripts', 'start-slice.sh')} ${path.join(projectRoot, 'specs', projectSlug, 'slices', 'slice-template', 'slice.json')}`);
|
|
280
|
+
console.log(`- Validate a slice with ${path.join(projectRoot, 'tools', 'scripts', 'check-slice-readiness.sh')}`);
|
|
281
|
+
console.log(`- Validate the PR gate with ${path.join(projectRoot, 'tools', 'scripts', 'check-pr-readiness.sh')}`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function printInitNextSteps(targetDir, projectName) {
|
|
285
|
+
const projectSlug = toProjectSlug(projectName);
|
|
286
|
+
|
|
287
|
+
console.log('');
|
|
288
|
+
console.log('Next steps:');
|
|
289
|
+
console.log(`- Review ${path.join(targetDir, 'docs', 'INDEX.md')}`);
|
|
290
|
+
console.log(`- Review ${path.join(targetDir, 'docs', 'WORKFLOW.md')}`);
|
|
291
|
+
console.log(`- Create your first slice from ${path.join(targetDir, 'specs', projectSlug, 'slices', 'slice-template', 'slice.json')}`);
|
|
292
|
+
console.log(`- Launch slice work with ${path.join(targetDir, 'tools', 'scripts', 'start-slice.sh')}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function run(argv) {
|
|
296
|
+
const args = parseArgs(argv);
|
|
297
|
+
|
|
298
|
+
if (args.help) {
|
|
299
|
+
printUsage();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (args.mode === 'doctor') {
|
|
304
|
+
runDoctor(args.targetDir);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const packageRoot = path.resolve(__dirname, '../..');
|
|
309
|
+
const targetDir = path.resolve(process.cwd(), args.targetDir);
|
|
310
|
+
const projectName = args.projectName || path.basename(targetDir) || 'Quiver Project';
|
|
311
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'quiver-create-'));
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
ensureDir(targetDir);
|
|
315
|
+
|
|
316
|
+
const templateRoot = packTemplate(packageRoot, tempRoot);
|
|
317
|
+
copyTemplate(templateRoot, targetDir);
|
|
318
|
+
runInitDocs(targetDir, projectName);
|
|
319
|
+
|
|
320
|
+
console.log(`Installed Quiver into ${targetDir}`);
|
|
321
|
+
printInitNextSteps(targetDir, projectName);
|
|
322
|
+
} finally {
|
|
323
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
module.exports = {
|
|
328
|
+
run,
|
|
329
|
+
};
|