scrumrun 1.5.2 → 2.1.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 +64 -0
- package/CORE.md +231 -308
- package/DECISIONS.md +151 -11
- package/LICENSE +21 -0
- package/MIGRATION-1-to-2.md +120 -0
- package/README.md +147 -78
- package/SPEC.md +259 -251
- package/bin/claude-install.js +18 -132
- package/bin/scrumrun.js +1367 -174
- package/docs/COMMANDS.md +71 -0
- package/docs/ENTITY-MODEL.md +38 -0
- package/docs/RELEASE-SCORECARD.md +43 -0
- package/docs/RELEASE.md +51 -0
- package/docs/SCHEMA.md +89 -0
- package/docs/SEMANTIC-MEMORY.md +68 -0
- package/docs/TROUBLESHOOTING.md +78 -0
- package/lib/code-intel/adapter.js +20 -0
- package/lib/code-intel/javascript.js +199 -0
- package/lib/code-intel/learning.js +66 -0
- package/lib/code-intel/scanner.js +126 -0
- package/lib/commands/manifest.js +118 -0
- package/lib/commands/render.js +78 -0
- package/lib/memory/index.js +659 -0
- package/lib/memory/markdown.js +51 -0
- package/lib/memory/service.js +300 -0
- package/lib/runtime/budgets.js +23 -0
- package/lib/runtime/canonical-snapshot.js +110 -0
- package/lib/runtime/context.js +101 -0
- package/lib/runtime/orchestrator.js +303 -0
- package/lib/runtime/policy-engine.js +184 -0
- package/lib/runtime/request-engine.js +132 -0
- package/lib/runtime/run-ledger.js +324 -0
- package/lib/security/secrets.js +23 -0
- package/lib/v2/artifacts.js +363 -0
- package/lib/v2/conformance.js +214 -0
- package/lib/v2/migration.js +1221 -0
- package/lib/v2/project-store.js +44 -0
- package/lib/v2/run-ledger-migration.js +240 -0
- package/lib/v2/schema.js +148 -0
- package/lib/v2/transaction.js +254 -0
- package/package.json +16 -6
- package/scripts/generate-contract-docs.js +124 -0
- package/templates/project/.scrumrun/config.md +4 -7
- package/templates/project/.scrumrun/guardrails.md +31 -0
- package/templates/project/.scrumrun/map.md +5 -16
- package/templates/project/.scrumrun/memory/decisions/.gitkeep +1 -0
- package/templates/project/.scrumrun/memory/dossiers/.gitkeep +1 -0
- package/templates/project/.scrumrun/memory/insights/.gitkeep +1 -0
- package/templates/project/.scrumrun/memory/knowledge/.gitkeep +1 -0
- package/templates/project/.scrumrun/method.json +7 -0
- package/templates/project/.scrumrun/project.md +6 -12
- package/templates/project/.scrumrun/runs/.gitkeep +1 -0
- package/templates/project/.scrumrun/sprints/.gitkeep +1 -0
- package/templates/project/.scrumrun/state.md +14 -0
- package/templates/project/.scrumrun/tasks/.gitkeep +1 -0
- package/templates/project/AGENTS.md +16 -49
- package/templates/project-lean/AGENTS.md +18 -0
- package/templates/shared/skills/scrumrun/SKILL.md +211 -0
- package/templates/codex/prompts/sc-agent.md +0 -14
- package/templates/codex/prompts/sc-backlog.md +0 -13
- package/templates/codex/prompts/sc-challenge.md +0 -57
- package/templates/codex/prompts/sc-config.md +0 -18
- package/templates/codex/prompts/sc-context.md +0 -24
- package/templates/codex/prompts/sc-decisions.md +0 -8
- package/templates/codex/prompts/sc-feature.md +0 -16
- package/templates/codex/prompts/sc-fix.md +0 -21
- package/templates/codex/prompts/sc-goal.md +0 -14
- package/templates/codex/prompts/sc-golden.md +0 -14
- package/templates/codex/prompts/sc-help.md +0 -12
- package/templates/codex/prompts/sc-init.md +0 -14
- package/templates/codex/prompts/sc-intake.md +0 -22
- package/templates/codex/prompts/sc-know.md +0 -75
- package/templates/codex/prompts/sc-map.md +0 -13
- package/templates/codex/prompts/sc-review.md +0 -13
- package/templates/codex/prompts/sc-sprint.md +0 -28
- package/templates/codex/prompts/sc-study.md +0 -23
- package/templates/codex/prompts/sc-uninstall.md +0 -14
- package/templates/codex/prompts/sc-update.md +0 -8
- package/templates/codex/prompts/sc-vault.md +0 -27
- package/templates/codex/skills/scrumrun/SKILL.md +0 -412
- package/templates/opencode/commands/sc-agent.md +0 -14
- package/templates/opencode/commands/sc-backlog.md +0 -13
- package/templates/opencode/commands/sc-challenge.md +0 -57
- package/templates/opencode/commands/sc-config.md +0 -18
- package/templates/opencode/commands/sc-context.md +0 -24
- package/templates/opencode/commands/sc-decisions.md +0 -8
- package/templates/opencode/commands/sc-feature.md +0 -16
- package/templates/opencode/commands/sc-fix.md +0 -21
- package/templates/opencode/commands/sc-goal.md +0 -14
- package/templates/opencode/commands/sc-golden.md +0 -14
- package/templates/opencode/commands/sc-help.md +0 -12
- package/templates/opencode/commands/sc-init.md +0 -14
- package/templates/opencode/commands/sc-intake.md +0 -22
- package/templates/opencode/commands/sc-know.md +0 -75
- package/templates/opencode/commands/sc-map.md +0 -13
- package/templates/opencode/commands/sc-review.md +0 -13
- package/templates/opencode/commands/sc-sprint.md +0 -28
- package/templates/opencode/commands/sc-study.md +0 -23
- package/templates/opencode/commands/sc-uninstall.md +0 -14
- package/templates/opencode/commands/sc-update.md +0 -8
- package/templates/opencode/commands/sc-vault.md +0 -27
- package/templates/opencode/skills/scrumrun/SKILL.md +0 -412
- package/templates/project/.scrumrun/agents.md +0 -36
- package/templates/project/.scrumrun/backlog.md +0 -7
- package/templates/project/.scrumrun/context.md +0 -61
- package/templates/project/.scrumrun/goals/main/decisions.md +0 -9
- package/templates/project/.scrumrun/goals/main/history.md +0 -51
- package/templates/project/.scrumrun/goals/main/sprint.md +0 -54
- package/templates/project/.scrumrun/golden-rules.md +0 -9
- package/templates/project/.scrumrun/knowledge.md +0 -15
- package/templates/project/.scrumrun/runbook.md +0 -101
- package/templates/project/.scrumrun/token-policy.md +0 -43
package/docs/COMMANDS.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# ScrumRun 2.0 Command Reference
|
|
2
|
+
|
|
3
|
+
The canonical grammar is:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
/sc <noun> <subject> <action> [args]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Use `/sc` inside a supported AI client. The equivalent CLI form is `npx scrumrun@latest sc ...`. CLI-native workflows execute immediately; reasoning-heavy routes tell the installed agent to execute the validated workflow.
|
|
10
|
+
|
|
11
|
+
## Plan
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
/sc plan intake <request>
|
|
15
|
+
/sc plan intake --approve <token>
|
|
16
|
+
/sc plan task --add|--list|--show|--run|--audit|--cancel|--retry
|
|
17
|
+
/sc plan sprint --add|--list|--show|--start|--complete|--block
|
|
18
|
+
/sc plan feature --add|--list|--show|--activate|--complete
|
|
19
|
+
/sc plan run --list|--show|--validate|--learn|--complete|--resume|--fail|--block [--note] [typed evidence flags]
|
|
20
|
+
/sc plan challenge <question>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
CLI-native: intake/approval, Task/Run list/show, Task retry, and Run transitions. A retry requires a failed, blocked, or partial Task and creates a new Run.
|
|
24
|
+
|
|
25
|
+
## Knowledge
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
/sc knowledge fact --add|--list|--show|--approve|--reject|--deprecate|--invalidate
|
|
29
|
+
/sc knowledge decision --add|--list|--show|--resolve|--deprecate|--invalidate
|
|
30
|
+
/sc knowledge insight --propose|--list|--show|--confirm|--stale|--reject|--deprecate|--invalidate
|
|
31
|
+
/sc knowledge dossier --add|--list|--show|--refresh|--stale|--deprecate|--archive
|
|
32
|
+
/sc knowledge context --build|--update|--show|--clear
|
|
33
|
+
/sc knowledge map --build|--show
|
|
34
|
+
/sc knowledge study <focus>
|
|
35
|
+
/sc knowledge vault --add|--list|--show|--remove|--path
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Creation options include `--title`, `--content`, repeated `--evidence`, repeated `--relation "used_by: Target"`, `--subject type:id`, `--source`, `--source-id`, `--confidence`, `--valid-from`, `--valid-until`, and `--review-trigger`. Promotion options accept repeated `--evidence` and `--note`.
|
|
39
|
+
|
|
40
|
+
`study` queries the semantic index. `map --build` regenerates both SQLite and bounded `map.md`. `context --clear` deletes only the disposable index.
|
|
41
|
+
|
|
42
|
+
## Rules and review
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
/sc rules guardrail --add|--list|--show|--retire
|
|
46
|
+
/sc rules reviewer --add|--list|--show|--run
|
|
47
|
+
/sc review code --run
|
|
48
|
+
/sc review artifact --run
|
|
49
|
+
/sc review migration --run
|
|
50
|
+
/sc review release --run
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`review artifact --run` is CLI-native and returns a machine-readable 20-invariant project audit. Other review routes require repository reasoning and remain read-only unless fixes receive separate approval.
|
|
54
|
+
|
|
55
|
+
## Config and lifecycle
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
/sc config project --show|--language|--interaction|--approval|--quick-tasks
|
|
59
|
+
/sc config init --local|--shared|--lean|--no-agent-hint|--force
|
|
60
|
+
/sc config update [all|codex|opencode|claude] [--migrate]
|
|
61
|
+
/sc config migrate --to 2 --dry-run|--apply|--rollback
|
|
62
|
+
/sc config doctor [all|codex|opencode|claude] [--strict] [--recover]
|
|
63
|
+
/sc config uninstall --force
|
|
64
|
+
/sc config help <topic>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Top-level CLI aliases (`init`, `update`, `migrate`, `doctor`, `uninstall`, `status`) remain available for shell automation. Ordinary update runs only a read-only migration preflight; `--migrate` is explicit application consent.
|
|
68
|
+
|
|
69
|
+
Run transitions accept typed evidence through `--command`, `--test`, `--file`, `--review`, `--decision`, `--insight`, `--risk`, or generic `--evidence kind:value`. `doctor --recover` is an explicit write that resolves only safe pending kernel transactions; doctor without it remains read-only.
|
|
70
|
+
|
|
71
|
+
Run `npx scrumrun@latest commands` for grammar rendered directly from the current manifest.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# ScrumRun 2.0 Entity Model
|
|
2
|
+
|
|
3
|
+
This document explains the model. Exact ids, directories, initial states, transitions, structural cardinalities, truth ownership, and authority boundaries are generated from `lib/v2/schema.js` into [`SCHEMA.md`](SCHEMA.md). Do not maintain another hand-written schema table here.
|
|
4
|
+
|
|
5
|
+
## Core distinction
|
|
6
|
+
|
|
7
|
+
| Entity | Question | Lifetime |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Feature | Why does this initiative matter? | Long-lived |
|
|
10
|
+
| Task | What atomic outcome is approved? | Until outcome/closure |
|
|
11
|
+
| Sprint | When are related Tasks grouped? | Timebox/batch |
|
|
12
|
+
| Run | How did one execution attempt happen? | Immutable attempt history |
|
|
13
|
+
| Memory | What do we know, and why? | Reviewed until stale/deprecated/invalidated |
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
FEAT-003
|
|
17
|
+
└── TASK-018
|
|
18
|
+
├── executed_by → RUN-044
|
|
19
|
+
├── included_in → SPRINT-012
|
|
20
|
+
├── constrained_by → DEC-018
|
|
21
|
+
└── generated → INS-041
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Task is always the executable unit. A Task may be independent of a Sprint. Sprint never substitutes for Task. A retry creates another Run for the same Task.
|
|
25
|
+
|
|
26
|
+
## Canonical artifacts
|
|
27
|
+
|
|
28
|
+
All artifacts carry `id`, `kind`, `status`, `created`, `updated`, and `method`. Relations live in frontmatter for structural ownership and in `## Relations` for extensible graph edges. The generated [`SCHEMA.md`](SCHEMA.md) is the authoritative inventory.
|
|
29
|
+
|
|
30
|
+
## Operational flow
|
|
31
|
+
|
|
32
|
+
Approval creates one Task and one Run. The Run progresses `executing → validating → learning → completed|failed|blocked`, while the Task mirrors `running → validating → learning → completed|failed|blocked`. Pair mutations are recoverable.
|
|
33
|
+
|
|
34
|
+
Feature and Sprint provide context/grouping and do not own execution history. Review attaches evidence. Memory explains decisions and constraints across all of them.
|
|
35
|
+
|
|
36
|
+
## Generated projections
|
|
37
|
+
|
|
38
|
+
`state.md` summarizes active work/memory. `map.md` summarizes bounded nodes/edges. SQLite stores the complete derived graph/search index. All are fingerprinted or explicitly stale and can be rebuilt from Markdown plus source code. Freshness uses a metadata watch fast path and content-hash fallback; the watch is disposable evidence, never truth.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ScrumRun 2.1.0 Local Release Scorecard
|
|
2
|
+
|
|
3
|
+
Date: 2026-07-22
|
|
4
|
+
Package: `2.1.0`
|
|
5
|
+
Method contract: `2.0.0`
|
|
6
|
+
Scope: local implementation and package readiness; external registry/tag/release verification remains owner-gated.
|
|
7
|
+
|
|
8
|
+
## Scoring rule
|
|
9
|
+
|
|
10
|
+
A score of 9.5 or higher requires a single documented contract, machine enforcement on the critical path, adversarial/failure tests, deterministic recovery where mutation is involved, bounded performance, and an explicit residual-risk statement. Documentation alone cannot earn 9.5.
|
|
11
|
+
|
|
12
|
+
| Area | Score | Executable evidence |
|
|
13
|
+
|---|---:|---|
|
|
14
|
+
| Conceptual model | 9.7 | Frozen Feature → Task → Sprint → Run → Memory schema; Task/Sprint and retry invariants; ADR-015 and ADR-018. |
|
|
15
|
+
| Documentation architecture | 9.7 | SPEC is normative, CORE is operational, ADRs explain trade-offs, generated SCHEMA is drift-checked, README/skill/templates are conformance-tested. |
|
|
16
|
+
| Artifact kernel | 9.7 | One executable schema, safe-path checks, lossless frontmatter transitions, conflict refusal, fsync atomic writes, and durable multi-file transactions with failure injection. |
|
|
17
|
+
| Run history and audit | 9.8 | Stable ordered event ids, RFC3339 timestamps, typed evidence, state reconstruction, completion gates, retry preservation, explicit early-v2 migration, and byte-exact rollback. |
|
|
18
|
+
| Real conformance | 9.7 | Twenty normative invariants point to executable tests; clean-project audit, malformed artifacts, secrets, symlinks, migration interruption, transaction interruption, and cache corruption are exercised. |
|
|
19
|
+
| Local state and retrieval | 9.6 | Intake/state share one canonical fingerprint; state/map/SQLite expose staleness; metadata fast path falls back to full content hashes; cache schema mismatch rebuilds once; stale map display is refused. |
|
|
20
|
+
| Overall operation | 9.6 | Read-only intake, explicit approval, atomic Task/Run creation, Policy Engine ids, migration preflight/apply/rollback, package E2E, installed-asset doctor, Node 22/24/26 CI definition, and release budgets. |
|
|
21
|
+
|
|
22
|
+
Minimum local score: **9.6/10**.
|
|
23
|
+
|
|
24
|
+
## Release evidence
|
|
25
|
+
|
|
26
|
+
- Full suite: `npm test`.
|
|
27
|
+
- Performance suite: `npm run benchmark`.
|
|
28
|
+
- Contract drift: `scripts/generate-contract-docs.js --check` runs before tests.
|
|
29
|
+
- Project conformance: `/sc review artifact --run`, twenty invariants, zero findings at the release checkpoint.
|
|
30
|
+
- Installed integration: `doctor codex --strict`, exact prompt/skill hashes and zero project findings.
|
|
31
|
+
- Package boundary: `npm pack --dry-run --json`, explicit file inventory, no repository-local `.scrumrun/`, tests, vault, backup, migration state, or cache.
|
|
32
|
+
- Tarball E2E: install, v2 memory, ongoing v1 migration, rollback, doctor, and uninstall run from the packed package in the test suite.
|
|
33
|
+
|
|
34
|
+
The exact final tarball checksum belongs in the owner-gated release Review/Run after all included files are frozen; embedding a tarball's own checksum inside an included document would change that checksum.
|
|
35
|
+
|
|
36
|
+
## Residual risks and gates
|
|
37
|
+
|
|
38
|
+
- Registry smoke, npm dist-tags, `v2.1.0` tag, push, and GitHub release are not proven by local tests and require explicit owner authorization.
|
|
39
|
+
- The built-in code-intelligence adapter currently covers JavaScript/TypeScript; other languages require replaceable adapters.
|
|
40
|
+
- Remote Node 22/24/26 CI must pass on the release commit even though the same matrix is declared locally in `.github/workflows/ci.yml`.
|
|
41
|
+
- Semantic retrieval is intentionally lexical/structural rather than a probabilistic embedding system; confirmed Markdown evidence remains the authority.
|
|
42
|
+
|
|
43
|
+
These are bounded release or extension risks, not hidden correctness claims. A failed external gate stops promotion and results in a new immutable SemVer; it never rewrites an existing npm version.
|
package/docs/RELEASE.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# ScrumRun Package Release Procedure
|
|
2
|
+
|
|
3
|
+
Publication is owner-gated. Tests, packaging, local commits, and release metadata preparation do not authorize npm publication, git push, tags, GitHub releases, or dist-tag changes.
|
|
4
|
+
|
|
5
|
+
The npm package/CLI follows SemVer independently from the ScrumRun method contract. For this release, package `2.1.0` implements method `2.0.0`. Published package `2.0.0` is immutable and must never be overwritten or reused.
|
|
6
|
+
|
|
7
|
+
## Local release gate
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm test
|
|
11
|
+
npm run benchmark
|
|
12
|
+
git diff --check
|
|
13
|
+
npm pack --dry-run
|
|
14
|
+
npx scrumrun@latest sc review artifact --run # in a clean v2 fixture/current package equivalent
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Confirm package contents exclude repository-local `.scrumrun/` state, caches, migration records, backups, vaults, tests, and secrets. The `.scrumrun/` directory inside project templates is expected. Confirm package metadata, README, changelog, tarball filename, checksum, and Git tag agree on `2.1.0`; SPEC, CORE, artifact frontmatter, migration, and installed skill continue to declare method `2.0.0`.
|
|
18
|
+
|
|
19
|
+
Record the local evidence and residual risks against [`RELEASE-SCORECARD.md`](./RELEASE-SCORECARD.md). Scores describe local readiness only and never replace registry smoke or owner approval.
|
|
20
|
+
|
|
21
|
+
## Optional registry candidate (separate owner approval required)
|
|
22
|
+
|
|
23
|
+
1. Set a new unpublished prerelease such as `2.1.0-rc.1` and create a reviewed commit.
|
|
24
|
+
2. `npm pack`; record tarball SHA-256/integrity.
|
|
25
|
+
3. Publish to `next`, never `latest`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm publish --tag next
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
4. Install from the registry into a clean fixture.
|
|
32
|
+
5. Run init, intake/approval, memory confirmation/query, v1 update preflight/apply/rollback, doctor, and uninstall.
|
|
33
|
+
6. Fix findings in a new RC; never replace an already-published version.
|
|
34
|
+
|
|
35
|
+
An RC is immutable. Fixes create `rc.2`, `rc.3`, and so on. Skipping an RC does not skip any local, tarball, owner, registry-smoke, or final-promotion gate.
|
|
36
|
+
|
|
37
|
+
## Final release (new owner approval required)
|
|
38
|
+
|
|
39
|
+
1. Set `2.1.0`, rerun every local gate, inspect the exact tarball, and create the final local metadata commit.
|
|
40
|
+
2. Stop and request explicit owner authorization for each external boundary.
|
|
41
|
+
3. Publish npm `2.1.0` to `next` using the reviewed tarball/source commit (`npm publish --tag next`); never publish from a changed worktree and do not move `latest` yet.
|
|
42
|
+
4. Install the registry artifact in a clean fixture and run init, intake/approval, Run lifecycle, memory query, v1 migration apply/rollback, doctor, and uninstall.
|
|
43
|
+
5. Only after registry smoke passes, create/push tag `v2.1.0`, promote/verify `latest` with an explicit dist-tag command, and create the GitHub release from `CHANGELOG.md` with the migration guide and checksum.
|
|
44
|
+
6. Verify npm metadata, dist-tags, Git tag/commit, GitHub release, README, and checksums all agree.
|
|
45
|
+
|
|
46
|
+
## Recovery
|
|
47
|
+
|
|
48
|
+
- RC defect: publish another RC; keep `latest` unchanged.
|
|
49
|
+
- Final package defect before `latest`: do not promote; publish a new patch after correction because `2.1.0` cannot be replaced.
|
|
50
|
+
- Defect after `latest`: assess deprecation vs immediate patch; never overwrite/unpublish without explicit owner decision and current npm-policy review.
|
|
51
|
+
- Migration issue: stop promotion, preserve registry artifact/checksum, use the documented rollback fixture, and publish a corrected version.
|
package/docs/SCHEMA.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# ScrumRun 2.0.0 Executable Schema
|
|
2
|
+
|
|
3
|
+
> Generated by `scripts/generate-contract-docs.js` from `lib/v2/schema.js`. Do not edit this file manually.
|
|
4
|
+
|
|
5
|
+
## Authority boundaries
|
|
6
|
+
|
|
7
|
+
| Concern | Authoritative source | Scope |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| semantics | `SPEC.md` | normative meanings and invariants |
|
|
10
|
+
| schema | `lib/v2/schema.js` | machine-enforced ids, paths, statuses, transitions, relations, and ownership metadata |
|
|
11
|
+
| commands | `lib/commands/manifest.js` | public command grammar and compatibility routes |
|
|
12
|
+
| projectPolicy | `.scrumrun/guardrails.md` | owner/project constraints |
|
|
13
|
+
| projectTruth | `.scrumrun/**/*.md` | authored canonical project records |
|
|
14
|
+
| projections | `.scrumrun/state.md, .scrumrun/map.md, .scrumrun/.cache/` | disposable derived views; never authority |
|
|
15
|
+
|
|
16
|
+
The boundaries above are deliberately different: SPEC owns meanings, the executable schema owns mechanically enforced values, the command manifest owns grammar, project Markdown owns authored project truth, and generated views own nothing.
|
|
17
|
+
|
|
18
|
+
## Artifact contract
|
|
19
|
+
|
|
20
|
+
| Kind | Stable ID | Canonical directory | Allowed initial status | Truth owned by this artifact |
|
|
21
|
+
|---|---|---|---|---|
|
|
22
|
+
| feature | `FEAT-NNN` | `features/` | `backlog`, `proposed` | initiative purpose, scope, dependencies, and lifecycle |
|
|
23
|
+
| task | `TASK-NNN` | `tasks/` | `backlog`, `proposed`, `running` | scope, acceptance criteria, approval, and intended status |
|
|
24
|
+
| sprint | `SPRINT-NNN` | `sprints/` | `proposed` | timebox or delivery-batch membership; never execution history |
|
|
25
|
+
| run | `RUN-NNN` | `runs/` | `executing` | append-only execution events, evidence, result, and attempt number |
|
|
26
|
+
| review | `REV-NNN` | `reviews/` | `proposed` | scoped findings, checks, evidence, and verdict |
|
|
27
|
+
| knowledge | `K-NNN` | `memory/knowledge/` | `candidate` | approved evidence-backed fact and validity |
|
|
28
|
+
| decision | `DEC-NNN` | `memory/decisions/` | `open` | decision, rationale, validity, and lifecycle |
|
|
29
|
+
| insight | `INS-NNN` | `memory/insights/` | `candidate` | explanatory candidate/confirmed context and evidence |
|
|
30
|
+
| dossier | `DOS-NNN` | `memory/dossiers/` | `active` | reviewed topic bundle and freshness |
|
|
31
|
+
|
|
32
|
+
Every artifact also requires `id`, `kind`, `status`, `created`, `updated`, and `method: 2.0.0`.
|
|
33
|
+
|
|
34
|
+
## Structural cardinalities
|
|
35
|
+
|
|
36
|
+
| Frontmatter field | Target | Cardinality | Meaning |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `feature` | feature (`FEAT-NNN`) | 0..1 | long-lived initiative containing the artifact |
|
|
39
|
+
| `sprint` | sprint (`SPRINT-NNN`) | 0..1 | optional delivery batch containing a Task or Run |
|
|
40
|
+
| `task` | task (`TASK-NNN`) | 1 for Run; otherwise 0..1 | atomic work executed or reviewed by the artifact |
|
|
41
|
+
|
|
42
|
+
Task is the atomic unit. A Task may have zero or one Sprint. A Task may have many Runs, but every Run belongs to exactly one Task and has a monotonically increasing attempt number within that Task. Sprint membership is authoritative on `Task.sprint`; a Sprint's `## Tasks` list is a human-readable projection that must agree with it.
|
|
43
|
+
|
|
44
|
+
## Scalar constraints
|
|
45
|
+
|
|
46
|
+
| Field | Kinds | Presence | Type | Meaning |
|
|
47
|
+
|---|---|---|---|---|
|
|
48
|
+
| `attempt` | run | required | positive integer | monotonic execution-attempt number within one Task |
|
|
49
|
+
| `ledger` | run | optional | integer 1 | canonical Run event-ledger schema; required for newly authored Runs |
|
|
50
|
+
|
|
51
|
+
Native creation uses the declared initial statuses. Migration may restore a historical non-initial status only with provenance and validation.
|
|
52
|
+
|
|
53
|
+
## Run event ledger
|
|
54
|
+
|
|
55
|
+
Newly authored Runs use `ledger: 1`. Their `## Events` section contains append-only JSON event blocks with stable ids in the form `RUN-NNN-EVT-NNN`.
|
|
56
|
+
|
|
57
|
+
Every event requires `schema`, `id`, contiguous `sequence`, RFC3339 `occurred_at`, `timestamp_precision`, `actor`, `from`, `to`, `reason`, and structured `evidence`. Event types are `transition`, `snapshot`. Evidence kinds are `approval`, `command`, `test`, `file`, `review`, `decision`, `insight`, `risk`, `note`, `migration`, `legacy`.
|
|
58
|
+
|
|
59
|
+
A native ledger begins with `created → executing`; an evidenced migration `snapshot` may establish one historical baseline without inventing missing transitions. Event order, transition legality, final status, updated date, and completion evidence are machine-validated. Run owns the event history; Task stores its intended scope and synchronized current status without copying Run events.
|
|
60
|
+
|
|
61
|
+
## Truth questions
|
|
62
|
+
|
|
63
|
+
- **feature:** Why does this initiative exist?
|
|
64
|
+
- **task:** What approved atomic outcome is intended?
|
|
65
|
+
- **sprint:** When are related Tasks grouped?
|
|
66
|
+
- **run:** How did one execution attempt actually happen?
|
|
67
|
+
- **review:** What independent validation was performed?
|
|
68
|
+
- **knowledge:** What verified project fact is reusable?
|
|
69
|
+
- **decision:** What normative choice constrains future work?
|
|
70
|
+
- **insight:** Why is something arranged or constrained this way?
|
|
71
|
+
- **dossier:** What evidence belongs to one retrieval topic?
|
|
72
|
+
|
|
73
|
+
## Lifecycles
|
|
74
|
+
|
|
75
|
+
| Kind | Declared transitions |
|
|
76
|
+
|---|---|
|
|
77
|
+
| feature | `backlog` → `proposed`, `active`, `cancelled`<br>`proposed` → `active`, `cancelled`<br>`active` → `paused`, `completed`, `cancelled`<br>`completed` → terminal<br>`paused` → `active`, `cancelled`<br>`cancelled` → terminal |
|
|
78
|
+
| task | `backlog` → `proposed`, `running`, `cancelled`<br>`proposed` → `running`, `cancelled`<br>`running` → `validating`, `failed`, `blocked`, `cancelled`<br>`validating` → `learning`, `failed`, `blocked`<br>`learning` → `completed`, `failed`, `blocked`<br>`partial` → `running`, `cancelled`<br>`completed` → terminal<br>`failed` → `running`, `cancelled`<br>`blocked` → `running`, `cancelled`<br>`cancelled` → terminal |
|
|
79
|
+
| sprint | `proposed` → `running`, `cancelled`<br>`running` → `partial`, `completed`, `blocked`, `cancelled`<br>`partial` → `running`, `completed`, `cancelled`<br>`completed` → terminal<br>`blocked` → `running`, `cancelled`<br>`cancelled` → terminal |
|
|
80
|
+
| run | `executing` → `validating`, `failed`, `blocked`<br>`validating` → `learning`, `failed`, `blocked`<br>`learning` → `completed`, `failed`, `blocked`<br>`partial` → `executing`, `failed`, `blocked`<br>`completed` → terminal<br>`failed` → terminal<br>`blocked` → `executing`, `failed` |
|
|
81
|
+
| review | `proposed` → `running`<br>`running` → `passed`, `failed`<br>`passed` → `archived`<br>`failed` → `running`, `archived`<br>`archived` → terminal |
|
|
82
|
+
| knowledge | `candidate` → `approved`, `rejected`<br>`approved` → `deprecated`, `invalidated`<br>`rejected` → `candidate`<br>`deprecated` → `approved`<br>`invalidated` → terminal |
|
|
83
|
+
| decision | `open` → `resolved`, `deprecated`, `invalidated`<br>`resolved` → `deprecated`, `invalidated`<br>`deprecated` → `open`<br>`invalidated` → terminal |
|
|
84
|
+
| insight | `candidate` → `confirmed`, `invalidated`<br>`confirmed` → `stale`, `deprecated`, `invalidated`<br>`stale` → `confirmed`, `deprecated`, `invalidated`<br>`deprecated` → `confirmed`<br>`invalidated` → terminal |
|
|
85
|
+
| dossier | `active` → `stale`, `deprecated`, `archived`<br>`stale` → `active`, `deprecated`, `archived`<br>`deprecated` → `archived`<br>`archived` → terminal |
|
|
86
|
+
|
|
87
|
+
## Projections
|
|
88
|
+
|
|
89
|
+
`state.md`, `map.md`, context packages, and `.cache/` are disposable. They may summarize or index canonical artifacts, but they cannot introduce status, policy, relations, decisions, or knowledge.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Semantic Memory
|
|
2
|
+
|
|
3
|
+
ScrumRun memory explains why code exists in its current form. It separates operational events, normative Decisions, reviewed facts, and contextual Insights so an agent can retrieve the smallest relevant evidence package.
|
|
4
|
+
|
|
5
|
+
## Choosing a kind
|
|
6
|
+
|
|
7
|
+
- Knowledge: descriptive fact verified by evidence.
|
|
8
|
+
- Decision: normative constraint—what must or must not happen.
|
|
9
|
+
- Insight: explanation, rationale, trade-off, warning, failure history, or testing note.
|
|
10
|
+
- Dossier: curated evidence bundle for a topic/module.
|
|
11
|
+
|
|
12
|
+
“Pricing calculations must run on the backend” is a Decision. “This function stays in pricing because quotations use it before checkout exists” is an Insight.
|
|
13
|
+
|
|
14
|
+
## Evidence and lifecycle
|
|
15
|
+
|
|
16
|
+
AI-created facts/insights always start as candidates. Confirmation requires one or more resolvable artifact IDs or project paths. Paths cannot traverse outside the project, follow symlinks, or reference the vault.
|
|
17
|
+
|
|
18
|
+
Useful metadata:
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
subject_type: symbol
|
|
22
|
+
subject_id: function:calculateFinalPrice
|
|
23
|
+
source_type: run
|
|
24
|
+
source_id: RUN-044
|
|
25
|
+
confidence: 0.8
|
|
26
|
+
valid_from: 2026-01-01
|
|
27
|
+
valid_until: null
|
|
28
|
+
last_verified_commit: <git-hash-or-unavailable>
|
|
29
|
+
review_trigger: pricing implementation changes
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Confirmed memory may become stale, deprecated, or invalidated. Those records remain auditable. Queries exclude rejected/deprecated/invalidated truth by default and label stale evidence explicitly.
|
|
33
|
+
|
|
34
|
+
## Relations and code graph
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
symbol:calculateFinalPrice
|
|
38
|
+
├── defined_in → module:pricing
|
|
39
|
+
├── depends_on → TaxCalculator
|
|
40
|
+
├── used_by → OrderService
|
|
41
|
+
├── constrained_by → DEC-018
|
|
42
|
+
├── has_insight → INS-041
|
|
43
|
+
└── protected_by → price-calculation.spec.ts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The JS/TS adapter records qualified symbol identity, path, kind, line, fingerprint, commit, exports, and lexical/module provenance. Moves with identical fingerprints remap. Unprovable rename/removal becomes orphaned history. Evidence or implementation drift marks linked records stale only in the projection; canonical Markdown changes only through explicit review.
|
|
47
|
+
|
|
48
|
+
## Learning from Runs
|
|
49
|
+
|
|
50
|
+
A validated Run may contain:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
## Learning Candidates
|
|
54
|
+
|
|
55
|
+
- [placement_rationale] function:calculateFinalPrice | Keep pricing in the domain because quotations use it. | evidence: src/quotation/service.ts | relations: used_by=module:src/quotation/service.ts
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Entering `learning` extracts `INS-NNN` candidates. Malformed extraction emits a warning and never blocks the Run.
|
|
59
|
+
|
|
60
|
+
## Query and maintenance
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx scrumrun@latest sc knowledge map --build
|
|
64
|
+
npx scrumrun@latest sc knowledge study calculateFinalPrice
|
|
65
|
+
npx scrumrun@latest sc knowledge context --clear
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
SQLite is ignored and disposable. Queries default to 10 records/40 relations and hard-cap at 100/100. Match type, truth state, warnings, relation counts, and evidence are returned so recommendations remain explainable.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## “Project must be explicitly migrated to v2”
|
|
4
|
+
|
|
5
|
+
Run `npx scrumrun@latest update` for a read-only preflight, then review and apply with `update --migrate`. See [`MIGRATION-1-to-2.md`](../MIGRATION-1-to-2.md).
|
|
6
|
+
|
|
7
|
+
## Migration is blocked
|
|
8
|
+
|
|
9
|
+
- Symlink: replace it with a regular local file/directory or preserve it outside `.scrumrun/`.
|
|
10
|
+
- Malformed `method.json`: do not guess; restore the correct v1 state/marker and rerun dry-run.
|
|
11
|
+
- Secret outside vault: move the value to `.scrumrun/vault.local.md`, redact the canonical source, and retry.
|
|
12
|
+
- Ambiguous history: warnings are expected and preserved; blockers must be resolved before apply.
|
|
13
|
+
|
|
14
|
+
Dry-run is safe to repeat and writes nothing.
|
|
15
|
+
|
|
16
|
+
## Rollback refuses
|
|
17
|
+
|
|
18
|
+
Rollback detected post-migration work that it would erase. Copy/export wanted changes, reconcile the paths named in the error, and retry. Never delete the migration backup to force rollback.
|
|
19
|
+
|
|
20
|
+
## `state.md` or `map.md` is stale
|
|
21
|
+
|
|
22
|
+
`state.md` refreshes after runtime transitions. Rebuild the semantic map with:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx scrumrun@latest sc knowledge map --build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Fresh projections include a source fingerprint and a watch fingerprint. A matching watch avoids a full read. If file metadata changed, ScrumRun hashes canonical/source content before deciding whether the projection is actually stale. `check: "schema"` means the disposable SQLite format changed and one rebuild is required; `check: "hash"` means the safe fallback was used.
|
|
29
|
+
|
|
30
|
+
Stale generated views are warnings, not canonical corruption.
|
|
31
|
+
|
|
32
|
+
## Doctor reports `TRANSACTION_PENDING`
|
|
33
|
+
|
|
34
|
+
An approved multi-file mutation was interrupted after its durable journal was prepared. Ordinary doctor/audit is read-only and leaves the evidence untouched. Either retry the same approved operation, which recovers before writing, or explicitly run:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx scrumrun@latest doctor codex --recover --strict
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Prepared transactions restore their original bytes; committed transactions verify the applied bytes and finalize. Recovery refuses if a target changed to content matching neither journal side, because that would overwrite later owner work. Receipts contain hashes and outcomes, not file contents.
|
|
41
|
+
|
|
42
|
+
## SQLite is missing/corrupt
|
|
43
|
+
|
|
44
|
+
Delete or clear only the disposable cache:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx scrumrun@latest sc knowledge context --clear
|
|
48
|
+
npx scrumrun@latest sc knowledge map --build
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Canonical Markdown is unchanged. Query automatically rebuilds a missing/stale index.
|
|
52
|
+
|
|
53
|
+
## Memory cannot be confirmed
|
|
54
|
+
|
|
55
|
+
Every confirmed fact/insight/Decision needs resolvable evidence. Use an existing artifact ID such as `RUN-044`/`DEC-018` or a regular project path. Absolute paths, traversal, symlinks, missing files, and vault references are rejected.
|
|
56
|
+
|
|
57
|
+
## A query returns stale/orphaned memory
|
|
58
|
+
|
|
59
|
+
Inspect its invalidation warning and code/evidence relations. For a real code move, rebuild may show `remappedFrom`. For a rename/removal without proof, the old symbol remains orphaned. Review the canonical memory; mark stale and reconfirm with current evidence, or invalidate it.
|
|
60
|
+
|
|
61
|
+
## Doctor reports missing clients
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx scrumrun@latest install codex
|
|
65
|
+
npx scrumrun@latest install claude
|
|
66
|
+
npx scrumrun@latest install opencode
|
|
67
|
+
npx scrumrun@latest doctor all
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Use `doctor --compat` only while validating one-cycle v1 adapters.
|
|
71
|
+
|
|
72
|
+
## Node.js is unsupported
|
|
73
|
+
|
|
74
|
+
ScrumRun 2.0 requires Node.js `>=22.13.0` because semantic indexing uses native `node:sqlite`. Upgrade Node, then rerun doctor.
|
|
75
|
+
|
|
76
|
+
## Safe uninstall
|
|
77
|
+
|
|
78
|
+
`uninstall` previews. `uninstall --force` removes `.scrumrun/` and only removes `AGENTS.md` when it is recognized as ScrumRun-generated. Export any memory you want to keep first.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
class LanguageAdapter {
|
|
4
|
+
constructor({ id, extensions }) {
|
|
5
|
+
if (!id || !Array.isArray(extensions) || !extensions.length) throw new Error("LanguageAdapter requires id and extensions.");
|
|
6
|
+
this.id = id;
|
|
7
|
+
this.extensions = new Set(extensions.map((extension) => extension.toLowerCase()));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
supports(file) {
|
|
11
|
+
const extension = file.slice(file.lastIndexOf(".")).toLowerCase();
|
|
12
|
+
return this.extensions.has(extension);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
scan() {
|
|
16
|
+
throw new Error(`${this.id} adapter must implement scan().`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { LanguageAdapter };
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const { LanguageAdapter } = require("./adapter");
|
|
6
|
+
const { sha256 } = require("../v2/artifacts");
|
|
7
|
+
|
|
8
|
+
function posix(value) {
|
|
9
|
+
return value.split(path.sep).join("/");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function maskSource(source) {
|
|
13
|
+
const chars = [...source];
|
|
14
|
+
let state = "code";
|
|
15
|
+
let escaped = false;
|
|
16
|
+
for (let index = 0; index < chars.length; index++) {
|
|
17
|
+
const char = chars[index];
|
|
18
|
+
const next = chars[index + 1];
|
|
19
|
+
if (state === "line") {
|
|
20
|
+
if (char === "\n") state = "code";
|
|
21
|
+
else chars[index] = " ";
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (state === "block") {
|
|
25
|
+
if (char === "*" && next === "/") {
|
|
26
|
+
chars[index] = chars[index + 1] = " ";
|
|
27
|
+
index++;
|
|
28
|
+
state = "code";
|
|
29
|
+
} else if (char !== "\n") chars[index] = " ";
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (["single", "double", "template"].includes(state)) {
|
|
33
|
+
if (char !== "\n") chars[index] = " ";
|
|
34
|
+
if (escaped) {
|
|
35
|
+
escaped = false;
|
|
36
|
+
} else if (char === "\\") {
|
|
37
|
+
escaped = true;
|
|
38
|
+
} else if ((state === "single" && char === "'") || (state === "double" && char === '"') || (state === "template" && char === "`")) {
|
|
39
|
+
state = "code";
|
|
40
|
+
}
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (char === "/" && next === "/") {
|
|
44
|
+
chars[index] = chars[index + 1] = " ";
|
|
45
|
+
index++;
|
|
46
|
+
state = "line";
|
|
47
|
+
} else if (char === "/" && next === "*") {
|
|
48
|
+
chars[index] = chars[index + 1] = " ";
|
|
49
|
+
index++;
|
|
50
|
+
state = "block";
|
|
51
|
+
} else if (char === "'") {
|
|
52
|
+
chars[index] = " ";
|
|
53
|
+
state = "single";
|
|
54
|
+
} else if (char === '"') {
|
|
55
|
+
chars[index] = " ";
|
|
56
|
+
state = "double";
|
|
57
|
+
} else if (char === "`") {
|
|
58
|
+
chars[index] = " ";
|
|
59
|
+
state = "template";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return chars.join("");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function closingBrace(masked, open) {
|
|
66
|
+
if (open < 0 || masked[open] !== "{") return -1;
|
|
67
|
+
let depth = 0;
|
|
68
|
+
for (let index = open; index < masked.length; index++) {
|
|
69
|
+
if (masked[index] === "{") depth++;
|
|
70
|
+
else if (masked[index] === "}" && --depth === 0) return index + 1;
|
|
71
|
+
}
|
|
72
|
+
return -1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function declarationEnd(masked, start) {
|
|
76
|
+
const open = masked.indexOf("{", start);
|
|
77
|
+
const semicolon = masked.indexOf(";", start);
|
|
78
|
+
if (open >= 0 && (semicolon < 0 || open < semicolon)) {
|
|
79
|
+
const close = closingBrace(masked, open);
|
|
80
|
+
if (close >= 0) return close;
|
|
81
|
+
}
|
|
82
|
+
const newline = masked.indexOf("\n", start);
|
|
83
|
+
return semicolon >= 0 ? semicolon + 1 : newline >= 0 ? newline : masked.length;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function lineAt(source, index) {
|
|
87
|
+
return source.slice(0, index).split("\n").length;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function declarations(source) {
|
|
91
|
+
const masked = maskSource(source);
|
|
92
|
+
const patterns = [
|
|
93
|
+
{ kind: "function", regex: /\b(?:export\s+(?:default\s+)?)?(?:async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)\s*\(/g },
|
|
94
|
+
{ kind: "class", regex: /\b(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)\b/g },
|
|
95
|
+
{ kind: "interface", regex: /\b(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)\b/g },
|
|
96
|
+
{ kind: "type", regex: /\b(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*(?:<[^;=]+>)?\s*=/g },
|
|
97
|
+
{ kind: "enum", regex: /\b(?:export\s+)?(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)\b/g },
|
|
98
|
+
{ kind: "function", regex: /\b(?:export\s+(?:default\s+)?)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::[^=;]+)?=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/g }
|
|
99
|
+
];
|
|
100
|
+
const found = [];
|
|
101
|
+
const seen = new Set();
|
|
102
|
+
for (const pattern of patterns) {
|
|
103
|
+
pattern.regex.lastIndex = 0;
|
|
104
|
+
let match;
|
|
105
|
+
while ((match = pattern.regex.exec(masked))) {
|
|
106
|
+
const key = `${match.index}:${match[1]}`;
|
|
107
|
+
if (seen.has(key)) continue;
|
|
108
|
+
seen.add(key);
|
|
109
|
+
const end = declarationEnd(masked, match.index);
|
|
110
|
+
const normalized = masked.slice(match.index, end).replace(/\s+/g, " ").trim();
|
|
111
|
+
found.push({
|
|
112
|
+
name: match[1],
|
|
113
|
+
kind: pattern.kind,
|
|
114
|
+
line: lineAt(source, match.index),
|
|
115
|
+
start: match.index,
|
|
116
|
+
end,
|
|
117
|
+
exported: /^\s*export\b/.test(masked.slice(Math.max(0, match.index - 20), match.index + 20)) || /\bexport\b/.test(match[0]),
|
|
118
|
+
fingerprint: sha256(`${pattern.kind}\0${normalized}`)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { masked, declarations: found.sort((a, b) => a.start - b.start) };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function importSpecifiers(source) {
|
|
126
|
+
const specifiers = new Set();
|
|
127
|
+
const patterns = [
|
|
128
|
+
/^\s*(?:import|export)\s+(?:[^"'\n]*?\s+from\s+)?["']([^"']+)["']/gm,
|
|
129
|
+
/\brequire\s*\(\s*["']([^"']+)["']\s*\)/g,
|
|
130
|
+
/\bimport\s*\(\s*["']([^"']+)["']\s*\)/g
|
|
131
|
+
];
|
|
132
|
+
for (const regex of patterns) {
|
|
133
|
+
let match;
|
|
134
|
+
while ((match = regex.exec(source))) specifiers.add(match[1]);
|
|
135
|
+
}
|
|
136
|
+
return [...specifiers].sort();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function resolveImport(projectRoot, sourceFile, specifier) {
|
|
140
|
+
if (!specifier.startsWith(".")) return `package:${specifier}`;
|
|
141
|
+
const base = path.resolve(path.dirname(sourceFile), specifier);
|
|
142
|
+
const candidates = [
|
|
143
|
+
base,
|
|
144
|
+
...[".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"].map((extension) => `${base}${extension}`),
|
|
145
|
+
...[".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"].map((extension) => path.join(base, `index${extension}`))
|
|
146
|
+
];
|
|
147
|
+
const target = candidates.find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isFile());
|
|
148
|
+
if (!target) return `module:${posix(path.relative(projectRoot, base))}`;
|
|
149
|
+
return `module:${posix(path.relative(projectRoot, target))}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class JavaScriptAdapter extends LanguageAdapter {
|
|
153
|
+
constructor() {
|
|
154
|
+
super({ id: "javascript-typescript", extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"] });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
scan({ projectRoot, file, relative, commit }) {
|
|
158
|
+
const source = fs.readFileSync(file, "utf8");
|
|
159
|
+
const parsed = declarations(source);
|
|
160
|
+
const moduleId = `module:${posix(relative)}`;
|
|
161
|
+
const dependencies = importSpecifiers(source).map((specifier) => ({
|
|
162
|
+
specifier,
|
|
163
|
+
target: resolveImport(projectRoot, file, specifier)
|
|
164
|
+
}));
|
|
165
|
+
const symbols = parsed.declarations.map((declaration) => ({
|
|
166
|
+
id: `symbol:${posix(relative)}#${declaration.kind}:${declaration.name}`,
|
|
167
|
+
name: declaration.name,
|
|
168
|
+
qualifiedName: `${posix(relative)}#${declaration.kind}:${declaration.name}`,
|
|
169
|
+
kind: declaration.kind,
|
|
170
|
+
path: posix(relative),
|
|
171
|
+
line: declaration.line,
|
|
172
|
+
fingerprint: declaration.fingerprint,
|
|
173
|
+
commit,
|
|
174
|
+
exported: declaration.exported,
|
|
175
|
+
language: this.id,
|
|
176
|
+
start: declaration.start,
|
|
177
|
+
end: declaration.end
|
|
178
|
+
}));
|
|
179
|
+
return {
|
|
180
|
+
module: {
|
|
181
|
+
id: moduleId,
|
|
182
|
+
name: posix(relative),
|
|
183
|
+
qualifiedName: posix(relative),
|
|
184
|
+
kind: "module",
|
|
185
|
+
path: posix(relative),
|
|
186
|
+
line: 1,
|
|
187
|
+
fingerprint: sha256(source),
|
|
188
|
+
commit,
|
|
189
|
+
exported: true,
|
|
190
|
+
language: this.id
|
|
191
|
+
},
|
|
192
|
+
symbols,
|
|
193
|
+
dependencies,
|
|
194
|
+
masked: parsed.masked
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = { JavaScriptAdapter, declarations, importSpecifiers, maskSource, resolveImport };
|