@encore-os/eos-spec 0.3.1
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/LICENSE +17 -0
- package/bin/check-dist-fresh.js +51 -0
- package/bin/eos-spec.js +7 -0
- package/dist/commands/ac-backfill.d.ts +31 -0
- package/dist/commands/ac-backfill.js +198 -0
- package/dist/commands/ac-backfill.js.map +1 -0
- package/dist/commands/archive.d.ts +2 -0
- package/dist/commands/archive.js +101 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/audit.d.ts +30 -0
- package/dist/commands/audit.js +107 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/backfill.d.ts +41 -0
- package/dist/commands/backfill.js +239 -0
- package/dist/commands/backfill.js.map +1 -0
- package/dist/commands/checkArtifacts.d.ts +2 -0
- package/dist/commands/checkArtifacts.js +48 -0
- package/dist/commands/checkArtifacts.js.map +1 -0
- package/dist/commands/checkPlacement.d.ts +2 -0
- package/dist/commands/checkPlacement.js +31 -0
- package/dist/commands/checkPlacement.js.map +1 -0
- package/dist/commands/claim.d.ts +4 -0
- package/dist/commands/claim.js +46 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/complete.d.ts +2 -0
- package/dist/commands/complete.js +138 -0
- package/dist/commands/complete.js.map +1 -0
- package/dist/commands/conformance.d.ts +23 -0
- package/dist/commands/conformance.js +124 -0
- package/dist/commands/conformance.js.map +1 -0
- package/dist/commands/create.d.ts +6 -0
- package/dist/commands/create.js +277 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/deferred.d.ts +2 -0
- package/dist/commands/deferred.js +85 -0
- package/dist/commands/deferred.js.map +1 -0
- package/dist/commands/delta.d.ts +17 -0
- package/dist/commands/delta.js +292 -0
- package/dist/commands/delta.js.map +1 -0
- package/dist/commands/freshness.d.ts +13 -0
- package/dist/commands/freshness.js +72 -0
- package/dist/commands/freshness.js.map +1 -0
- package/dist/commands/funnel.d.ts +2 -0
- package/dist/commands/funnel.js +61 -0
- package/dist/commands/funnel.js.map +1 -0
- package/dist/commands/gen-tests.d.ts +2 -0
- package/dist/commands/gen-tests.js +53 -0
- package/dist/commands/gen-tests.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +64 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +9 -0
- package/dist/commands/list.js +61 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/metrics.d.ts +2 -0
- package/dist/commands/metrics.js +66 -0
- package/dist/commands/metrics.js.map +1 -0
- package/dist/commands/next.d.ts +48 -0
- package/dist/commands/next.js +344 -0
- package/dist/commands/next.js.map +1 -0
- package/dist/commands/precedent.d.ts +66 -0
- package/dist/commands/precedent.js +343 -0
- package/dist/commands/precedent.js.map +1 -0
- package/dist/commands/ready.d.ts +28 -0
- package/dist/commands/ready.js +147 -0
- package/dist/commands/ready.js.map +1 -0
- package/dist/commands/reconcile.d.ts +44 -0
- package/dist/commands/reconcile.js +187 -0
- package/dist/commands/reconcile.js.map +1 -0
- package/dist/commands/rollup.d.ts +2 -0
- package/dist/commands/rollup.js +43 -0
- package/dist/commands/rollup.js.map +1 -0
- package/dist/commands/scorecard.d.ts +11 -0
- package/dist/commands/scorecard.js +55 -0
- package/dist/commands/scorecard.js.map +1 -0
- package/dist/commands/signoff.d.ts +2 -0
- package/dist/commands/signoff.js +114 -0
- package/dist/commands/signoff.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +100 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/tasks.d.ts +13 -0
- package/dist/commands/tasks.js +81 -0
- package/dist/commands/tasks.js.map +1 -0
- package/dist/commands/validate.d.ts +16 -0
- package/dist/commands/validate.js +89 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/verify-acs.d.ts +15 -0
- package/dist/commands/verify-acs.js +196 -0
- package/dist/commands/verify-acs.js.map +1 -0
- package/dist/commands/verifyRollout.d.ts +2 -0
- package/dist/commands/verifyRollout.js +125 -0
- package/dist/commands/verifyRollout.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/ac-draft-prompt.d.ts +18 -0
- package/dist/lib/ac-draft-prompt.js +38 -0
- package/dist/lib/ac-draft-prompt.js.map +1 -0
- package/dist/lib/ac-response-parser.d.ts +18 -0
- package/dist/lib/ac-response-parser.js +57 -0
- package/dist/lib/ac-response-parser.js.map +1 -0
- package/dist/lib/ai-call.d.ts +13 -0
- package/dist/lib/ai-call.js +17 -0
- package/dist/lib/ai-call.js.map +1 -0
- package/dist/lib/archive.d.ts +21 -0
- package/dist/lib/archive.js +136 -0
- package/dist/lib/archive.js.map +1 -0
- package/dist/lib/artifact-integrity.d.ts +5 -0
- package/dist/lib/artifact-integrity.js +65 -0
- package/dist/lib/artifact-integrity.js.map +1 -0
- package/dist/lib/backfill-queue.d.ts +11 -0
- package/dist/lib/backfill-queue.js +14 -0
- package/dist/lib/backfill-queue.js.map +1 -0
- package/dist/lib/blockers.d.ts +66 -0
- package/dist/lib/blockers.js +103 -0
- package/dist/lib/blockers.js.map +1 -0
- package/dist/lib/claims.d.ts +39 -0
- package/dist/lib/claims.js +133 -0
- package/dist/lib/claims.js.map +1 -0
- package/dist/lib/classify-debt.d.ts +17 -0
- package/dist/lib/classify-debt.js +13 -0
- package/dist/lib/classify-debt.js.map +1 -0
- package/dist/lib/code-evidence.d.ts +35 -0
- package/dist/lib/code-evidence.js +171 -0
- package/dist/lib/code-evidence.js.map +1 -0
- package/dist/lib/coherence.d.ts +14 -0
- package/dist/lib/coherence.js +188 -0
- package/dist/lib/coherence.js.map +1 -0
- package/dist/lib/completion-gate.d.ts +17 -0
- package/dist/lib/completion-gate.js +28 -0
- package/dist/lib/completion-gate.js.map +1 -0
- package/dist/lib/compliance-signoff.d.ts +39 -0
- package/dist/lib/compliance-signoff.js +81 -0
- package/dist/lib/compliance-signoff.js.map +1 -0
- package/dist/lib/config.d.ts +63 -0
- package/dist/lib/config.js +40 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/cost.d.ts +12 -0
- package/dist/lib/cost.js +12 -0
- package/dist/lib/cost.js.map +1 -0
- package/dist/lib/debt-dashboard.d.ts +25 -0
- package/dist/lib/debt-dashboard.js +36 -0
- package/dist/lib/debt-dashboard.js.map +1 -0
- package/dist/lib/embedding-store.d.ts +103 -0
- package/dist/lib/embedding-store.js +133 -0
- package/dist/lib/embedding-store.js.map +1 -0
- package/dist/lib/embeddings-source.d.ts +36 -0
- package/dist/lib/embeddings-source.js +62 -0
- package/dist/lib/embeddings-source.js.map +1 -0
- package/dist/lib/enforcement-allowlist.d.ts +12 -0
- package/dist/lib/enforcement-allowlist.js +25 -0
- package/dist/lib/enforcement-allowlist.js.map +1 -0
- package/dist/lib/events.d.ts +47 -0
- package/dist/lib/events.js +75 -0
- package/dist/lib/events.js.map +1 -0
- package/dist/lib/exec.d.ts +8 -0
- package/dist/lib/exec.js +25 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +112 -0
- package/dist/lib/frontmatter.js +261 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/funnel.d.ts +22 -0
- package/dist/lib/funnel.js +41 -0
- package/dist/lib/funnel.js.map +1 -0
- package/dist/lib/git-mtime.d.ts +4 -0
- package/dist/lib/git-mtime.js +23 -0
- package/dist/lib/git-mtime.js.map +1 -0
- package/dist/lib/metrics.d.ts +42 -0
- package/dist/lib/metrics.js +92 -0
- package/dist/lib/metrics.js.map +1 -0
- package/dist/lib/migration-collision.d.ts +20 -0
- package/dist/lib/migration-collision.js +61 -0
- package/dist/lib/migration-collision.js.map +1 -0
- package/dist/lib/next-action.d.ts +44 -0
- package/dist/lib/next-action.js +138 -0
- package/dist/lib/next-action.js.map +1 -0
- package/dist/lib/ollama-embed.d.ts +21 -0
- package/dist/lib/ollama-embed.js +65 -0
- package/dist/lib/ollama-embed.js.map +1 -0
- package/dist/lib/output.d.ts +35 -0
- package/dist/lib/output.js +46 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/pipeline-config.d.ts +20 -0
- package/dist/lib/pipeline-config.js +208 -0
- package/dist/lib/pipeline-config.js.map +1 -0
- package/dist/lib/pipeline-map.d.ts +68 -0
- package/dist/lib/pipeline-map.js +256 -0
- package/dist/lib/pipeline-map.js.map +1 -0
- package/dist/lib/pipeline-schema.d.ts +48 -0
- package/dist/lib/pipeline-schema.js +22 -0
- package/dist/lib/pipeline-schema.js.map +1 -0
- package/dist/lib/placement.d.ts +6 -0
- package/dist/lib/placement.js +40 -0
- package/dist/lib/placement.js.map +1 -0
- package/dist/lib/precedent-search.d.ts +18 -0
- package/dist/lib/precedent-search.js +160 -0
- package/dist/lib/precedent-search.js.map +1 -0
- package/dist/lib/predicate-registry.d.ts +6 -0
- package/dist/lib/predicate-registry.js +40 -0
- package/dist/lib/predicate-registry.js.map +1 -0
- package/dist/lib/recall-cases.d.ts +15 -0
- package/dist/lib/recall-cases.js +39 -0
- package/dist/lib/recall-cases.js.map +1 -0
- package/dist/lib/repo.d.ts +20 -0
- package/dist/lib/repo.js +82 -0
- package/dist/lib/repo.js.map +1 -0
- package/dist/lib/requirements.d.ts +91 -0
- package/dist/lib/requirements.js +179 -0
- package/dist/lib/requirements.js.map +1 -0
- package/dist/lib/rollup.d.ts +24 -0
- package/dist/lib/rollup.js +34 -0
- package/dist/lib/rollup.js.map +1 -0
- package/dist/lib/scorecard-run.d.ts +6 -0
- package/dist/lib/scorecard-run.js +48 -0
- package/dist/lib/scorecard-run.js.map +1 -0
- package/dist/lib/scorecard.d.ts +69 -0
- package/dist/lib/scorecard.js +113 -0
- package/dist/lib/scorecard.js.map +1 -0
- package/dist/lib/semantic-search.d.ts +36 -0
- package/dist/lib/semantic-search.js +56 -0
- package/dist/lib/semantic-search.js.map +1 -0
- package/dist/lib/session-state.d.ts +8 -0
- package/dist/lib/session-state.js +25 -0
- package/dist/lib/session-state.js.map +1 -0
- package/dist/lib/spec-debt.d.ts +8 -0
- package/dist/lib/spec-debt.js +17 -0
- package/dist/lib/spec-debt.js.map +1 -0
- package/dist/lib/spec-pure.d.ts +59 -0
- package/dist/lib/spec-pure.js +215 -0
- package/dist/lib/spec-pure.js.map +1 -0
- package/dist/lib/specs.d.ts +148 -0
- package/dist/lib/specs.js +373 -0
- package/dist/lib/specs.js.map +1 -0
- package/dist/lib/test-generator.d.ts +5 -0
- package/dist/lib/test-generator.js +72 -0
- package/dist/lib/test-generator.js.map +1 -0
- package/dist/lib/user-stories.d.ts +6 -0
- package/dist/lib/user-stories.js +56 -0
- package/dist/lib/user-stories.js.map +1 -0
- package/dist/lib/verifies-scan.d.ts +9 -0
- package/dist/lib/verifies-scan.js +73 -0
- package/dist/lib/verifies-scan.js.map +1 -0
- package/dist/lib/verify-core.d.ts +23 -0
- package/dist/lib/verify-core.js +48 -0
- package/dist/lib/verify-core.js.map +1 -0
- package/dist/lib/with-event-log.d.ts +12 -0
- package/dist/lib/with-event-log.js +65 -0
- package/dist/lib/with-event-log.js.map +1 -0
- package/dist/lib/wizard-specs.d.ts +17 -0
- package/dist/lib/wizard-specs.js +34 -0
- package/dist/lib/wizard-specs.js.map +1 -0
- package/dist/pure.d.ts +19 -0
- package/dist/pure.js +31 -0
- package/dist/pure.js.map +1 -0
- package/package.json +69 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { registerAcBackfill } from './commands/ac-backfill.js';
|
|
4
|
+
import { registerArchive } from './commands/archive.js';
|
|
5
|
+
import { registerAudit } from './commands/audit.js';
|
|
6
|
+
import { registerBackfill } from './commands/backfill.js';
|
|
7
|
+
import { registerCheckArtifacts } from './commands/checkArtifacts.js';
|
|
8
|
+
import { registerCheckPlacement } from './commands/checkPlacement.js';
|
|
9
|
+
import { registerClaim } from './commands/claim.js';
|
|
10
|
+
import { registerComplete } from './commands/complete.js';
|
|
11
|
+
import { registerConformance } from './commands/conformance.js';
|
|
12
|
+
import { registerCreate } from './commands/create.js';
|
|
13
|
+
import { registerDeferred } from './commands/deferred.js';
|
|
14
|
+
import { registerDelta } from './commands/delta.js';
|
|
15
|
+
import { registerFreshness } from './commands/freshness.js';
|
|
16
|
+
import { registerFunnel } from './commands/funnel.js';
|
|
17
|
+
import { registerGenTests } from './commands/gen-tests.js';
|
|
18
|
+
import { registerInit } from './commands/init.js';
|
|
19
|
+
import { registerList } from './commands/list.js';
|
|
20
|
+
import { registerMetrics } from './commands/metrics.js';
|
|
21
|
+
import { registerNext } from './commands/next.js';
|
|
22
|
+
import { registerPrecedent } from './commands/precedent.js';
|
|
23
|
+
import { registerReady } from './commands/ready.js';
|
|
24
|
+
import { registerReconcile } from './commands/reconcile.js';
|
|
25
|
+
import { registerReadinessRollup } from './commands/rollup.js';
|
|
26
|
+
import { registerScorecard } from './commands/scorecard.js';
|
|
27
|
+
import { registerSignoff } from './commands/signoff.js';
|
|
28
|
+
import { registerStatus } from './commands/status.js';
|
|
29
|
+
import { registerTasks } from './commands/tasks.js';
|
|
30
|
+
import { registerValidate } from './commands/validate.js';
|
|
31
|
+
import { registerVerifyAcs } from './commands/verify-acs.js';
|
|
32
|
+
import { registerVerifyRollout } from './commands/verifyRollout.js';
|
|
33
|
+
export { listSpecs, findSpec, findSpecsById, deriveSignals, diagnoseMissingSpec } from './lib/specs.js';
|
|
34
|
+
export { loadConfig, resetConfigCache, coresFromConfig } from './lib/config.js';
|
|
35
|
+
export { embeddingsSourceFromConfig, FilesystemEmbeddingsSource } from './lib/embeddings-source.js';
|
|
36
|
+
// ── Consumer surface (epic #300, Wave 3a — encoreos repoint) ─────────────────
|
|
37
|
+
// The other heavy consumer (encoreos: navigator evals, verification-debt and
|
|
38
|
+
// scorecard scripts, the navigator-SSOT and compliance/UI-evidence gates) reaches
|
|
39
|
+
// into the fs-bound corpus + navigator + scoring helpers. They live here on the
|
|
40
|
+
// main entry (not `/pure`) because they read the repo. The deterministic leaves
|
|
41
|
+
// among them (`computeStage`, `PIPELINE`, `isCompleteStatus`, `collectSpecIds`,
|
|
42
|
+
// `uiEvidenceGate`, `parseComplianceSignoff`, `SessionState`) are ALSO exported
|
|
43
|
+
// from `/pure`; they are re-exported here so a single import site suffices for a
|
|
44
|
+
// server/Node consumer that does not care about the client boundary.
|
|
45
|
+
export { recommendAction } from './commands/next.js';
|
|
46
|
+
export { getRepoRoot, coresList } from './lib/repo.js';
|
|
47
|
+
export { buildScorecardForCore } from './lib/scorecard-run.js';
|
|
48
|
+
export { scanVerifies } from './lib/verifies-scan.js';
|
|
49
|
+
export { computeStage, PIPELINE } from './lib/pipeline-map.js';
|
|
50
|
+
export { isCompleteStatus, collectSpecIds } from './lib/blockers.js';
|
|
51
|
+
export { uiEvidenceGate } from './lib/completion-gate.js';
|
|
52
|
+
export { parseComplianceSignoff } from './lib/compliance-signoff.js';
|
|
53
|
+
/** The fully-registered CLI program. Exported (not parsed on import — the parse
|
|
54
|
+
* call below is guarded by the main-module check) so the command-surface gate
|
|
55
|
+
* and conformance pack can enumerate the registered command set. */
|
|
56
|
+
export const program = new Command();
|
|
57
|
+
program
|
|
58
|
+
.name('eos-spec')
|
|
59
|
+
.description('Encore OS spec workflow CLI — agent-friendly wrapper around the existing spec, audit, and validation scripts. All commands accept `--json` for machine-readable output.')
|
|
60
|
+
.version('0.3.1')
|
|
61
|
+
.option('--repo-root <path>', 'Override repo root (defaults to git rev-parse --show-toplevel)')
|
|
62
|
+
.option('--config <path>', 'Override specs/config.yaml path (consumed by init, create, delta, verify-rollout; validated for all commands)')
|
|
63
|
+
.option('--json', 'Output JSON instead of human-readable text', false)
|
|
64
|
+
.option('--quiet', 'Suppress non-essential output', false);
|
|
65
|
+
program.hook('preAction', () => {
|
|
66
|
+
const { config } = program.opts();
|
|
67
|
+
if (config && !existsSync(config)) {
|
|
68
|
+
console.error(`eos-spec: config file not found: ${config}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
registerAcBackfill(program);
|
|
73
|
+
registerInit(program);
|
|
74
|
+
registerValidate(program);
|
|
75
|
+
registerNext(program);
|
|
76
|
+
registerReady(program);
|
|
77
|
+
registerClaim(program);
|
|
78
|
+
registerPrecedent(program);
|
|
79
|
+
registerStatus(program);
|
|
80
|
+
registerList(program);
|
|
81
|
+
registerCreate(program);
|
|
82
|
+
registerTasks(program);
|
|
83
|
+
registerComplete(program);
|
|
84
|
+
registerArchive(program);
|
|
85
|
+
registerDeferred(program);
|
|
86
|
+
registerDelta(program);
|
|
87
|
+
registerFreshness(program);
|
|
88
|
+
registerFunnel(program);
|
|
89
|
+
registerScorecard(program);
|
|
90
|
+
registerBackfill(program);
|
|
91
|
+
registerReadinessRollup(program);
|
|
92
|
+
registerMetrics(program);
|
|
93
|
+
registerVerifyRollout(program);
|
|
94
|
+
registerCheckPlacement(program);
|
|
95
|
+
registerCheckArtifacts(program);
|
|
96
|
+
registerReconcile(program);
|
|
97
|
+
registerSignoff(program);
|
|
98
|
+
registerVerifyAcs(program);
|
|
99
|
+
registerGenTests(program);
|
|
100
|
+
registerAudit(program);
|
|
101
|
+
registerConformance(program);
|
|
102
|
+
program.addHelpText('after', `
|
|
103
|
+
Examples:
|
|
104
|
+
$ eos-spec validate # Validate every spec across the repo
|
|
105
|
+
$ eos-spec validate --core cl --json # Validate CL specs, JSON output
|
|
106
|
+
$ eos-spec next --core hr # Show next recommended action for HR (blocker-aware)
|
|
107
|
+
$ eos-spec ready --core lo # List LO specs whose blockers are all Complete
|
|
108
|
+
$ eos-spec ready --propose --core lo # Propose blocked_by entries from legacy dependencies
|
|
109
|
+
$ eos-spec precedent --topic "billing reconciliation" --core fa
|
|
110
|
+
$ eos-spec status --spec PF-37 # Detail spec status (phases, tests, deferred)
|
|
111
|
+
$ eos-spec list --core pm --status InProgress
|
|
112
|
+
$ eos-spec create PF-99 "Foo Feature" # Scaffold spec/plan/tasks files (stubs)
|
|
113
|
+
$ eos-spec tasks PF-99 # Generate or refresh TASKS file
|
|
114
|
+
$ eos-spec complete --spec PF-37 --update-registry
|
|
115
|
+
$ eos-spec archive --spec PF-37 # Archive a completed spec
|
|
116
|
+
$ eos-spec deferred --sync # Sync deferred dashboard
|
|
117
|
+
$ eos-spec scorecard --core fm # Per-spec readiness; headline: claimed-complete-but-unverified
|
|
118
|
+
$ eos-spec backfill --core hr # Classify verification debt; write specs/hr/VERIFICATION_DEBT.md (dry-run)
|
|
119
|
+
$ eos-spec backfill --core hr --apply # Also downgrade overstated status lines (T1 relinks excluded)
|
|
120
|
+
$ eos-spec readiness-rollup --save # Ranked platform readiness rollup across all cores
|
|
121
|
+
$ eos-spec delta validate --spec CL-15-EN-01 # Validate change delta section (compound enhancement ID)
|
|
122
|
+
$ eos-spec verify-rollout # Run rollout verification checks
|
|
123
|
+
|
|
124
|
+
Documentation:
|
|
125
|
+
specs/_templates/SPEC_GUIDE.md # Spec authoring decision tree
|
|
126
|
+
specs/_templates/TEMPLATE_INDEX.md # Template selection
|
|
127
|
+
AI_GUIDE.md # Spec workflow process
|
|
128
|
+
`);
|
|
129
|
+
export async function run(argv) {
|
|
130
|
+
await program.parseAsync(argv);
|
|
131
|
+
}
|
|
132
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
133
|
+
run(process.argv).catch((err) => {
|
|
134
|
+
process.stderr.write(`${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type UserStory = {
|
|
2
|
+
id: string;
|
|
3
|
+
text: string;
|
|
4
|
+
};
|
|
5
|
+
export type AcDraftInput = {
|
|
6
|
+
specId: string;
|
|
7
|
+
core: string;
|
|
8
|
+
title: string;
|
|
9
|
+
userStories: UserStory[];
|
|
10
|
+
/** Optional excerpt from specs/{core}/research/{ID}-RESEARCH.md (trimmed). */
|
|
11
|
+
research?: string;
|
|
12
|
+
/** Highest existing AC ordinal in the spec; new ACs start at existingAcCount + 1. */
|
|
13
|
+
existingAcCount: number;
|
|
14
|
+
};
|
|
15
|
+
/** Build a single prompt that asks the model to draft Gherkin ACs for unaddressed user stories.
|
|
16
|
+
* The model must return STRICT JSON: { acceptance_criteria: [...] }.
|
|
17
|
+
* Drafting guidance mirrors automation/skills/encore-spec-ac-draft/SKILL.md. */
|
|
18
|
+
export declare function buildAcDraftPrompt(input: AcDraftInput): string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Build a single prompt that asks the model to draft Gherkin ACs for unaddressed user stories.
|
|
2
|
+
* The model must return STRICT JSON: { acceptance_criteria: [...] }.
|
|
3
|
+
* Drafting guidance mirrors automation/skills/encore-spec-ac-draft/SKILL.md. */
|
|
4
|
+
export function buildAcDraftPrompt(input) {
|
|
5
|
+
const start = input.existingAcCount + 1;
|
|
6
|
+
const stories = input.userStories.map((s) => `- ${s.id}: ${s.text}`).join('\n');
|
|
7
|
+
const research = input.research ? `\n\n## Research excerpt\n${input.research.slice(0, 2000)}` : '';
|
|
8
|
+
return [
|
|
9
|
+
`You are drafting Gherkin acceptance criteria for spec ${input.specId} (${input.core}) — "${input.title}".`,
|
|
10
|
+
'',
|
|
11
|
+
'Return STRICT JSON only — no prose, no markdown fences:',
|
|
12
|
+
'{',
|
|
13
|
+
' "acceptance_criteria": [',
|
|
14
|
+
' { "id": "AC-N", "title": "...", "user_story": "US-N", "status": "planned", "draft": true,',
|
|
15
|
+
' "given": ["...", "..."], "when": ["..."], "then": ["...", "..."] }',
|
|
16
|
+
' ]',
|
|
17
|
+
'}',
|
|
18
|
+
'',
|
|
19
|
+
`Start AC numbering at AC-${start}.`,
|
|
20
|
+
'',
|
|
21
|
+
'Draft 1–3 scenarios per user story.',
|
|
22
|
+
'- Given clauses describe state setup (who is logged in, what data exists).',
|
|
23
|
+
'- When clauses describe the user action (clicked button, submitted form).',
|
|
24
|
+
'- Then clauses describe observable results (text appeared, count changed).',
|
|
25
|
+
'- Stay concrete: "user clicks the Save button" beats "user saves".',
|
|
26
|
+
'',
|
|
27
|
+
'Locator priority (semantic for Playwright compatibility):',
|
|
28
|
+
'1. Role-based ("user clicks the Submit button")',
|
|
29
|
+
'2. Label-based ("user enters 123 in the Account Number field")',
|
|
30
|
+
'3. TestId-based ("user opens the claims list")',
|
|
31
|
+
'Avoid visual descriptions ("the red error box") and translation-volatile strings.',
|
|
32
|
+
'',
|
|
33
|
+
'## User stories',
|
|
34
|
+
stories,
|
|
35
|
+
research,
|
|
36
|
+
].join('\n');
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=ac-draft-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ac-draft-prompt.js","sourceRoot":"","sources":["../../src/lib/ac-draft-prompt.ts"],"names":[],"mappings":"AAaA;;iFAEiF;AACjF,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,MAAM,KAAK,GAAG,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChF,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,4BAA4B,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnG,OAAO;QACL,yDAAyD,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC,KAAK,IAAI;QAC3G,EAAE;QACF,yDAAyD;QACzD,GAAG;QACH,4BAA4B;QAC5B,+FAA+F;QAC/F,0EAA0E;QAC1E,KAAK;QACL,GAAG;QACH,EAAE;QACF,4BAA4B,KAAK,GAAG;QACpC,EAAE;QACF,qCAAqC;QACrC,4EAA4E;QAC5E,2EAA2E;QAC3E,4EAA4E;QAC5E,oEAAoE;QACpE,EAAE;QACF,2DAA2D;QAC3D,iDAAiD;QACjD,gEAAgE;QAChE,gDAAgD;QAChD,mFAAmF;QACnF,EAAE;QACF,iBAAiB;QACjB,OAAO;QACP,QAAQ;KACT,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type AcEntry = {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
user_story?: string;
|
|
5
|
+
status: 'planned';
|
|
6
|
+
draft: true;
|
|
7
|
+
given: string[];
|
|
8
|
+
when: string[];
|
|
9
|
+
then: string[];
|
|
10
|
+
};
|
|
11
|
+
export type ParseResult = {
|
|
12
|
+
ok: true;
|
|
13
|
+
acs: AcEntry[];
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
error: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function parseAcResponse(raw: string): ParseResult;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const REQUIRED = ['id', 'title', 'given', 'when', 'then'];
|
|
2
|
+
function isStringArray(v) {
|
|
3
|
+
return Array.isArray(v) && v.every((x) => typeof x === 'string') && v.length > 0;
|
|
4
|
+
}
|
|
5
|
+
export function parseAcResponse(raw) {
|
|
6
|
+
// Strip code fences if any.
|
|
7
|
+
let text = raw
|
|
8
|
+
.replace(/```(?:json)?\s*/gi, '')
|
|
9
|
+
.replace(/```/g, '')
|
|
10
|
+
.trim();
|
|
11
|
+
// Carve out the JSON object — first '{' to matching last '}'.
|
|
12
|
+
const start = text.indexOf('{');
|
|
13
|
+
const end = text.lastIndexOf('}');
|
|
14
|
+
if (start < 0 || end <= start)
|
|
15
|
+
return { ok: false, error: 'no JSON object found' };
|
|
16
|
+
text = text.slice(start, end + 1);
|
|
17
|
+
let parsed;
|
|
18
|
+
try {
|
|
19
|
+
parsed = JSON.parse(text);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return { ok: false, error: `JSON parse failed: ${e.message}` };
|
|
23
|
+
}
|
|
24
|
+
if (!parsed || typeof parsed !== 'object')
|
|
25
|
+
return { ok: false, error: 'parsed value is not an object' };
|
|
26
|
+
const list = parsed.acceptance_criteria;
|
|
27
|
+
if (!Array.isArray(list))
|
|
28
|
+
return { ok: false, error: 'acceptance_criteria missing or not an array' };
|
|
29
|
+
const acs = [];
|
|
30
|
+
for (const entry of list) {
|
|
31
|
+
if (!entry || typeof entry !== 'object')
|
|
32
|
+
return { ok: false, error: 'AC entry is not an object' };
|
|
33
|
+
const rec = entry;
|
|
34
|
+
for (const k of REQUIRED) {
|
|
35
|
+
if (rec[k] === undefined)
|
|
36
|
+
return { ok: false, error: `AC entry missing required field "${k}"` };
|
|
37
|
+
}
|
|
38
|
+
if (typeof rec.id !== 'string' || typeof rec.title !== 'string') {
|
|
39
|
+
return { ok: false, error: 'AC entry id/title must be strings' };
|
|
40
|
+
}
|
|
41
|
+
if (!isStringArray(rec.given) || !isStringArray(rec.when) || !isStringArray(rec.then)) {
|
|
42
|
+
return { ok: false, error: 'AC entry given/when/then must be non-empty string arrays' };
|
|
43
|
+
}
|
|
44
|
+
acs.push({
|
|
45
|
+
id: rec.id,
|
|
46
|
+
title: rec.title,
|
|
47
|
+
user_story: typeof rec.user_story === 'string' ? rec.user_story : undefined,
|
|
48
|
+
status: 'planned',
|
|
49
|
+
draft: true,
|
|
50
|
+
given: rec.given,
|
|
51
|
+
when: rec.when,
|
|
52
|
+
then: rec.then,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return { ok: true, acs };
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=ac-response-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ac-response-parser.js","sourceRoot":"","sources":["../../src/lib/ac-response-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type AiTokens } from './cost.js';
|
|
2
|
+
export type LogAiCallInput = {
|
|
3
|
+
command: string;
|
|
4
|
+
model: string;
|
|
5
|
+
ms: number;
|
|
6
|
+
ok: boolean;
|
|
7
|
+
tokens?: AiTokens;
|
|
8
|
+
error?: string;
|
|
9
|
+
ts?: string;
|
|
10
|
+
};
|
|
11
|
+
/** Log a single AI gateway call as an eos-spec event, computing USD cost from token counts.
|
|
12
|
+
* Shared by every AI-calling command so token/cost capture is uniform (DRY). */
|
|
13
|
+
export declare function logAiCall(input: LogAiCallInput): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { computeCostUsd } from './cost.js';
|
|
2
|
+
import { logEvent } from './events.js';
|
|
3
|
+
/** Log a single AI gateway call as an eos-spec event, computing USD cost from token counts.
|
|
4
|
+
* Shared by every AI-calling command so token/cost capture is uniform (DRY). */
|
|
5
|
+
export function logAiCall(input) {
|
|
6
|
+
logEvent({
|
|
7
|
+
ts: input.ts ?? new Date().toISOString(),
|
|
8
|
+
command: input.command,
|
|
9
|
+
model: input.model,
|
|
10
|
+
ms: input.ms,
|
|
11
|
+
ok: input.ok,
|
|
12
|
+
tokens: input.tokens,
|
|
13
|
+
costUsd: input.tokens ? computeCostUsd(input.model, input.tokens) : undefined,
|
|
14
|
+
error: input.error,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=ai-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-call.js","sourceRoot":"","sources":["../../src/lib/ai-call.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,cAAc,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAYvC;iFACiF;AACjF,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,QAAQ,CAAC;QACP,EAAE,EAAE,KAAK,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACxC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;QAC7E,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type SpecFile } from './specs.js';
|
|
2
|
+
export declare function isArchiveEligible(status: string | undefined): boolean;
|
|
3
|
+
export type ArchiveResult = {
|
|
4
|
+
specId: string;
|
|
5
|
+
moved: string[];
|
|
6
|
+
skipped: string[];
|
|
7
|
+
/** Set when the status guard refused to archive (run again with --force to override). */
|
|
8
|
+
refused?: boolean;
|
|
9
|
+
reason?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function archiveSpec(repoRoot: string, specIdOrFile: string | SpecFile, log: ((msg: string) => void) | null, configPath?: string, opts?: {
|
|
12
|
+
force?: boolean;
|
|
13
|
+
}): ArchiveResult;
|
|
14
|
+
export declare function getSpecLastModified(repoRoot: string, specPath: string): Date | null;
|
|
15
|
+
export declare function archiveSpecsByFilter(repoRoot: string, opts: {
|
|
16
|
+
core?: string;
|
|
17
|
+
status?: string;
|
|
18
|
+
olderThanDays?: number;
|
|
19
|
+
dryRun?: boolean;
|
|
20
|
+
force?: boolean;
|
|
21
|
+
}, log: ((msg: string) => void) | null, configPath?: string): ArchiveResult[];
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, renameSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { loadConfig } from './config.js';
|
|
4
|
+
import { runScript } from './exec.js';
|
|
5
|
+
import { findSpec, listSpecs } from './specs.js';
|
|
6
|
+
const DONE_STATUSES = ['complete', 'implemented'];
|
|
7
|
+
/** Statuses that make a spec eligible for physical archiving without --force.
|
|
8
|
+
* Matched as case-insensitive substrings because legacy body statuses carry
|
|
9
|
+
* decoration ("✅ Implemented", "Superseded by PF-12"). `deferred` is
|
|
10
|
+
* intentionally excluded: deferred specs live on the deferred dashboard and
|
|
11
|
+
* may be revived. */
|
|
12
|
+
const ARCHIVE_ELIGIBLE_STATUSES = ['complete', 'implemented', 'archived', 'superseded'];
|
|
13
|
+
export function isArchiveEligible(status) {
|
|
14
|
+
const lower = (status ?? '').toLowerCase();
|
|
15
|
+
return ARCHIVE_ELIGIBLE_STATUSES.some((kw) => lower.includes(kw));
|
|
16
|
+
}
|
|
17
|
+
export function archiveSpec(repoRoot, specIdOrFile, log, configPath, opts = {}) {
|
|
18
|
+
const spec = typeof specIdOrFile === 'string' ? findSpec(repoRoot, specIdOrFile) : specIdOrFile;
|
|
19
|
+
const specId = typeof specIdOrFile === 'string' ? specIdOrFile : specIdOrFile.id;
|
|
20
|
+
if (!spec) {
|
|
21
|
+
log?.(`archive: ${specId} not found, skipping`);
|
|
22
|
+
return { specId, moved: [], skipped: [] };
|
|
23
|
+
}
|
|
24
|
+
if (!opts.force && !isArchiveEligible(spec.status)) {
|
|
25
|
+
const reason = `status "${spec.status ?? '(none)'}" is not archive-eligible (${ARCHIVE_ELIGIBLE_STATUSES.join('/')})`;
|
|
26
|
+
log?.(`archive: refusing ${spec.id} — ${reason}; pass --force to override`);
|
|
27
|
+
return { specId: spec.id, moved: [], skipped: [], refused: true, reason };
|
|
28
|
+
}
|
|
29
|
+
let archiveSub = 'archive';
|
|
30
|
+
try {
|
|
31
|
+
const config = loadConfig(repoRoot, configPath);
|
|
32
|
+
archiveSub = config.paths.archive;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// use default
|
|
36
|
+
}
|
|
37
|
+
const archiveDir = join(repoRoot, 'specs', spec.core, archiveSub);
|
|
38
|
+
mkdirSync(archiveDir, { recursive: true });
|
|
39
|
+
const moved = [];
|
|
40
|
+
const skipped = [];
|
|
41
|
+
const candidates = collectCandidates(repoRoot, spec);
|
|
42
|
+
for (const src of candidates) {
|
|
43
|
+
if (!existsSync(src))
|
|
44
|
+
continue;
|
|
45
|
+
const filename = src.split('/').pop();
|
|
46
|
+
const dest = join(archiveDir, filename);
|
|
47
|
+
if (existsSync(dest)) {
|
|
48
|
+
log?.(`archive: skipping ${filename} (already in archive)`);
|
|
49
|
+
skipped.push(filename);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
renameSync(src, dest);
|
|
53
|
+
moved.push(dest);
|
|
54
|
+
log?.(`archive: moved ${filename} → specs/${spec.core}/${archiveSub}/`);
|
|
55
|
+
}
|
|
56
|
+
return { specId: spec.id, moved, skipped };
|
|
57
|
+
}
|
|
58
|
+
function collectCandidates(repoRoot, spec) {
|
|
59
|
+
const coreBase = join(repoRoot, 'specs', spec.core);
|
|
60
|
+
const prefix = `${spec.id}-`;
|
|
61
|
+
const candidates = new Set();
|
|
62
|
+
candidates.add(spec.path);
|
|
63
|
+
const contextFile = join(coreBase, 'specs', `${spec.id}-CONTEXT.md`);
|
|
64
|
+
if (existsSync(contextFile))
|
|
65
|
+
candidates.add(contextFile);
|
|
66
|
+
for (const sub of ['plans', 'tasks']) {
|
|
67
|
+
const dir = join(coreBase, sub);
|
|
68
|
+
if (!existsSync(dir))
|
|
69
|
+
continue;
|
|
70
|
+
for (const file of readdirSync(dir)) {
|
|
71
|
+
if (!file.endsWith('.md'))
|
|
72
|
+
continue;
|
|
73
|
+
if (file.startsWith(prefix) || file === `${spec.id}.md`) {
|
|
74
|
+
candidates.add(join(dir, file));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return [...candidates];
|
|
79
|
+
}
|
|
80
|
+
function statusMatches(specStatus, requested) {
|
|
81
|
+
const lower = (specStatus ?? '').toLowerCase();
|
|
82
|
+
if (requested === 'complete') {
|
|
83
|
+
if (lower.includes('deferred'))
|
|
84
|
+
return false;
|
|
85
|
+
return DONE_STATUSES.some((kw) => lower.includes(kw));
|
|
86
|
+
}
|
|
87
|
+
return lower.includes(requested.toLowerCase());
|
|
88
|
+
}
|
|
89
|
+
export function getSpecLastModified(repoRoot, specPath) {
|
|
90
|
+
const result = runScript(['git', 'log', '-1', '--format=%cI', '--', specPath], repoRoot);
|
|
91
|
+
const iso = result.stdout.trim();
|
|
92
|
+
if (!iso)
|
|
93
|
+
return null;
|
|
94
|
+
const parsed = new Date(iso);
|
|
95
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
96
|
+
}
|
|
97
|
+
export function archiveSpecsByFilter(repoRoot, opts, log, configPath) {
|
|
98
|
+
const specs = listSpecs(repoRoot, opts.core);
|
|
99
|
+
const results = [];
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
const filtered = specs.filter((s) => {
|
|
102
|
+
if (opts.status && !statusMatches(s.status, opts.status))
|
|
103
|
+
return false;
|
|
104
|
+
if (seen.has(s.id))
|
|
105
|
+
return false;
|
|
106
|
+
seen.add(s.id);
|
|
107
|
+
if (opts.olderThanDays != null) {
|
|
108
|
+
const lastMod = getSpecLastModified(repoRoot, s.path);
|
|
109
|
+
if (!lastMod)
|
|
110
|
+
return false;
|
|
111
|
+
const cutoff = new Date();
|
|
112
|
+
cutoff.setDate(cutoff.getDate() - opts.olderThanDays);
|
|
113
|
+
if (lastMod > cutoff) {
|
|
114
|
+
log?.(`archive: skipping ${s.id} (last modified ${lastMod.toISOString()}, within ${opts.olderThanDays} days)`);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
});
|
|
120
|
+
for (const spec of filtered) {
|
|
121
|
+
if (opts.dryRun) {
|
|
122
|
+
if (!opts.force && !isArchiveEligible(spec.status)) {
|
|
123
|
+
const reason = `status "${spec.status ?? '(none)'}" is not archive-eligible (${ARCHIVE_ELIGIBLE_STATUSES.join('/')})`;
|
|
124
|
+
log?.(`archive (dry-run): would refuse ${spec.id} — ${reason}`);
|
|
125
|
+
results.push({ specId: spec.id, moved: [], skipped: [], refused: true, reason });
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
log?.(`archive (dry-run): would archive ${spec.id}`);
|
|
129
|
+
results.push({ specId: spec.id, moved: [], skipped: [] });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
results.push(archiveSpec(repoRoot, spec, log, configPath, { force: opts.force }));
|
|
133
|
+
}
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/lib/archive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Collect primary artifact paths that require frontmatter:
|
|
5
|
+
* - automation/skills/*\/SKILL.md (top-level manifest only, no sub-docs)
|
|
6
|
+
* - automation/commands/*.md (top-level only, no recursion)
|
|
7
|
+
* - automation/agents/*.md (top-level only, no recursion)
|
|
8
|
+
*/
|
|
9
|
+
function collectPrimaryArtifacts(repoRoot) {
|
|
10
|
+
const files = [];
|
|
11
|
+
// Skills: only the SKILL.md inside each direct subdirectory
|
|
12
|
+
const skillsDir = join(repoRoot, 'automation', 'skills');
|
|
13
|
+
if (existsSync(skillsDir)) {
|
|
14
|
+
for (const e of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
15
|
+
if (e.isDirectory()) {
|
|
16
|
+
const skillMd = join(skillsDir, e.name, 'SKILL.md');
|
|
17
|
+
if (existsSync(skillMd))
|
|
18
|
+
files.push(skillMd);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Commands and agents: top-level *.md files only (no recursion)
|
|
23
|
+
for (const sub of ['commands', 'agents']) {
|
|
24
|
+
const dir = join(repoRoot, 'automation', sub);
|
|
25
|
+
if (!existsSync(dir))
|
|
26
|
+
continue;
|
|
27
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
28
|
+
if (!e.isDirectory() && e.name.endsWith('.md')) {
|
|
29
|
+
files.push(join(dir, e.name));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return files;
|
|
34
|
+
}
|
|
35
|
+
function walkSymlinks(dir, out) {
|
|
36
|
+
if (!existsSync(dir))
|
|
37
|
+
return;
|
|
38
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
39
|
+
const full = join(dir, e.name);
|
|
40
|
+
if (e.isSymbolicLink()) {
|
|
41
|
+
try {
|
|
42
|
+
realpathSync(full);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
out.push(full);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (e.isDirectory()) {
|
|
49
|
+
walkSymlinks(full, out);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function checkArtifactIntegrity(repoRoot) {
|
|
54
|
+
const mdFiles = collectPrimaryArtifacts(repoRoot);
|
|
55
|
+
const missingFrontmatter = mdFiles.filter((p) => {
|
|
56
|
+
const text = readFileSync(p, 'utf8');
|
|
57
|
+
return !/^---[ \t]*\n[\s\S]*?\bname:\s*\S/.test(text.slice(0, 600));
|
|
58
|
+
});
|
|
59
|
+
const danglingSymlinks = [];
|
|
60
|
+
for (const root of ['.claude', '.cursor']) {
|
|
61
|
+
walkSymlinks(join(repoRoot, root), danglingSymlinks);
|
|
62
|
+
}
|
|
63
|
+
return { missingFrontmatter, danglingSymlinks };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=artifact-integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-integrity.js","sourceRoot":"","sources":["../../src/lib/artifact-integrity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a prioritised work queue from a set of backfill rows, ordering T1 relinks
|
|
3
|
+
* before T2/T3/T4 rebuilds, and dropping T0 entries (already done).
|
|
4
|
+
*
|
|
5
|
+
* The generic keeps the full caller row shape so downstream callers don't need to
|
|
6
|
+
* re-join against the original data.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildQueue<T extends {
|
|
9
|
+
id: string;
|
|
10
|
+
tier: string;
|
|
11
|
+
}>(rows: T[]): T[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const ORDER = { T1: 0, T2: 1, T3: 2, T4: 3 };
|
|
2
|
+
/**
|
|
3
|
+
* Build a prioritised work queue from a set of backfill rows, ordering T1 relinks
|
|
4
|
+
* before T2/T3/T4 rebuilds, and dropping T0 entries (already done).
|
|
5
|
+
*
|
|
6
|
+
* The generic keeps the full caller row shape so downstream callers don't need to
|
|
7
|
+
* re-join against the original data.
|
|
8
|
+
*/
|
|
9
|
+
export function buildQueue(rows) {
|
|
10
|
+
return rows
|
|
11
|
+
.filter((r) => r.tier in ORDER)
|
|
12
|
+
.sort((a, b) => ORDER[a.tier] - ORDER[b.tier]);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=backfill-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backfill-queue.js","sourceRoot":"","sources":["../../src/lib/backfill-queue.ts"],"names":[],"mappings":"AAAA,MAAM,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAW,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAyC,IAAS;IAC1E,OAAO,IAAI;SACR,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC;SAC9B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAA0B,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAA0B,CAAC,CAAC,CAAC;AAC/F,CAAC"}
|