specrails-core 6.6.4 → 6.7.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/dist/agent-runtime/capabilities.d.ts +5 -2
- package/dist/agent-runtime/capabilities.js +8 -2
- package/dist/agent-runtime/change-scope.d.ts +2 -0
- package/dist/agent-runtime/change-scope.js +5 -0
- package/dist/agent-runtime/cli-executor.js +7 -0
- package/dist/agent-runtime/cli.d.ts +6 -1
- package/dist/agent-runtime/cli.js +15 -4
- package/dist/agent-runtime/compact/reviewer.js +3 -1
- package/dist/agent-runtime/config.js +16 -5
- package/dist/agent-runtime/efficiency-summary.d.ts +7 -0
- package/dist/agent-runtime/efficiency-summary.js +7 -1
- package/dist/agent-runtime/engine/__fixtures__/implementation-fixture.js +1 -1
- package/dist/agent-runtime/engine/checkpoint/database.d.ts +9 -0
- package/dist/agent-runtime/engine/checkpoint/database.js +40 -3
- package/dist/agent-runtime/engine/checkpoint/ledger.d.ts +1 -0
- package/dist/agent-runtime/engine/checkpoint/ledger.js +7 -2
- package/dist/agent-runtime/engine/cli.d.ts +12 -0
- package/dist/agent-runtime/engine/cli.js +15 -4
- package/dist/agent-runtime/engine/definition-schema.d.ts +1 -1
- package/dist/agent-runtime/engine/definition-schema.js +1 -0
- package/dist/agent-runtime/engine/definition-types.d.ts +1 -1
- package/dist/agent-runtime/engine/definition-types.js +1 -1
- package/dist/agent-runtime/engine/definition-validator.js +8 -0
- package/dist/agent-runtime/engine/efficiency-summary.d.ts +7 -0
- package/dist/agent-runtime/engine/graph-description.d.ts +3 -3
- package/dist/agent-runtime/engine/invocation-context.js +2 -0
- package/dist/agent-runtime/engine/piece-registry.d.ts +1 -1
- package/dist/agent-runtime/engine/piece-registry.js +1 -1
- package/dist/agent-runtime/engine/pieces/artifact-contract.d.ts +5 -0
- package/dist/agent-runtime/engine/pieces/artifact-contract.js +6 -2
- package/dist/agent-runtime/engine/pieces/derivation-coverage.d.ts +30 -0
- package/dist/agent-runtime/engine/pieces/derivation-coverage.js +95 -0
- package/dist/agent-runtime/engine/pieces/derive-change.d.ts +50 -0
- package/dist/agent-runtime/engine/pieces/derive-change.js +209 -0
- package/dist/agent-runtime/engine/pieces/index.js +2 -1
- package/dist/agent-runtime/engine/pieces/openspec.d.ts +16 -1
- package/dist/agent-runtime/engine/pieces/openspec.js +39 -30
- package/dist/agent-runtime/engine/pieces/role-turn.js +26 -5
- package/dist/agent-runtime/engine/preflight.d.ts +2 -1
- package/dist/agent-runtime/engine/preflight.js +3 -1
- package/dist/agent-runtime/engine/regex.d.ts +12 -2
- package/dist/agent-runtime/engine/regex.js +25 -11
- package/dist/agent-runtime/engine/run-status.d.ts +34 -0
- package/dist/agent-runtime/engine/run-status.js +7 -1
- package/dist/agent-runtime/engine/runs.d.ts +56 -0
- package/dist/agent-runtime/engine/runs.js +88 -5
- package/dist/agent-runtime/engine/steering/inbox.d.ts +15 -0
- package/dist/agent-runtime/engine/steering/inbox.js +21 -1
- package/dist/agent-runtime/engine/storage/private-path.js +7 -1
- package/dist/agent-runtime/evaluation.js +1 -1
- package/dist/agent-runtime/executor-types.d.ts +19 -0
- package/dist/agent-runtime/executor-types.js +10 -0
- package/dist/agent-runtime/failure-classification.d.ts +51 -0
- package/dist/agent-runtime/failure-classification.js +110 -0
- package/dist/agent-runtime/graph/archive-preflight.d.ts +3 -18
- package/dist/agent-runtime/graph/archive-preflight.js +6 -51
- package/dist/agent-runtime/graph/artifacts.d.ts +3 -1
- package/dist/agent-runtime/graph/artifacts.js +6 -2
- package/dist/agent-runtime/graph/baseline.d.ts +50 -0
- package/dist/agent-runtime/graph/baseline.js +145 -0
- package/dist/agent-runtime/graph/capability-map.d.ts +29 -0
- package/dist/agent-runtime/graph/capability-map.js +65 -0
- package/dist/agent-runtime/graph/clarifications.d.ts +75 -0
- package/dist/agent-runtime/graph/clarifications.js +158 -0
- package/dist/agent-runtime/graph/nodes.js +178 -19
- package/dist/agent-runtime/graph/review-brief-record.d.ts +25 -0
- package/dist/agent-runtime/graph/review-brief-record.js +26 -0
- package/dist/agent-runtime/graph/review-brief.d.ts +73 -0
- package/dist/agent-runtime/graph/review-brief.js +127 -0
- package/dist/agent-runtime/graph/roles.js +5 -3
- package/dist/agent-runtime/graph/spec-trace-record.d.ts +33 -0
- package/dist/agent-runtime/graph/spec-trace-record.js +65 -0
- package/dist/agent-runtime/graph/state.d.ts +8 -0
- package/dist/agent-runtime/index.d.ts +7 -0
- package/dist/agent-runtime/index.js +7 -0
- package/dist/agent-runtime/openspec.d.ts +2 -1
- package/dist/agent-runtime/openspec.js +11 -5
- package/dist/agent-runtime/project-rules.d.ts +139 -0
- package/dist/agent-runtime/project-rules.js +683 -0
- package/dist/agent-runtime/prompts.d.ts +32 -1
- package/dist/agent-runtime/prompts.js +180 -7
- package/dist/agent-runtime/role-routing.d.ts +2 -1
- package/dist/agent-runtime/session/application/active-session.d.ts +43 -2
- package/dist/agent-runtime/session/application/active-session.js +163 -7
- package/dist/agent-runtime/session/application/session-service.d.ts +2 -2
- package/dist/agent-runtime/session/domain/descriptor.js +5 -0
- package/dist/agent-runtime/session/domain/interruption.js +1 -0
- package/dist/agent-runtime/session/domain/policy.js +2 -0
- package/dist/agent-runtime/session/domain/redaction.d.ts +19 -0
- package/dist/agent-runtime/session/domain/redaction.js +71 -0
- package/dist/agent-runtime/session/domain/types.d.ts +17 -1
- package/dist/agent-runtime/session/drivers/claude/argv.d.ts +14 -0
- package/dist/agent-runtime/session/drivers/claude/argv.js +19 -2
- package/dist/agent-runtime/session/drivers/claude/driver.js +11 -1
- package/dist/agent-runtime/session/drivers/claude/translator.d.ts +4 -0
- package/dist/agent-runtime/session/drivers/claude/translator.js +16 -3
- package/dist/agent-runtime/session/drivers/codex/driver.js +2 -0
- package/dist/agent-runtime/session/drivers/executor/driver.js +2 -0
- package/dist/agent-runtime/session/host/protocol.d.ts +12 -0
- package/dist/agent-runtime/session/host/protocol.js +10 -2
- package/dist/agent-runtime/session/host/server.js +6 -5
- package/dist/agent-runtime/session/index.d.ts +1 -1
- package/dist/agent-runtime/session/journal/sqlite-journal.js +6 -2
- package/dist/agent-runtime/session/ports.d.ts +6 -2
- package/dist/agent-runtime/session/testing/driver-conformance.js +16 -2
- package/dist/agent-runtime/session/testing/memory-journal.js +4 -2
- package/dist/installer/cli.js +10 -1
- package/dist/installer/commands/init.js +15 -4
- package/dist/installer/phases/prereqs.d.ts +1 -1
- package/dist/installer/phases/prereqs.js +2 -2
- package/dist/installer/phases/scaffold.js +59 -11
- package/dist/pipeline/pipeline-state.d.ts +15 -0
- package/dist/pipeline/pipeline-state.js +36 -3
- package/dist/pipeline/spec-trace.d.ts +134 -0
- package/dist/pipeline/spec-trace.js +392 -0
- package/dist/shared/secret-redaction.d.ts +42 -0
- package/dist/shared/secret-redaction.js +184 -0
- package/integration-contract.json +177 -7
- package/package.json +8 -8
- package/pinned-versions.json +1 -1
- package/schemas/agent-runtime.schema.json +3 -1
- package/schemas/workflow-definition.schema.json +1 -0
- package/templates/settings/codex-config.toml +3 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type PipelineContext, type VerificationReceipt } from '../../pipeline/pipeline-state.js';
|
|
2
|
+
import { SPEC_TRACE_SCHEMA_VERSION, type SpecTraceReport } from '../../pipeline/spec-trace.js';
|
|
3
|
+
/** What the verification record, the reviewer and the acceptance findings carry; the full report stays in spec-trace.json. */
|
|
4
|
+
export interface SpecTraceSummary {
|
|
5
|
+
schemaVersion: typeof SPEC_TRACE_SCHEMA_VERSION;
|
|
6
|
+
receiptId: string | null;
|
|
7
|
+
summary: string;
|
|
8
|
+
requirements: number;
|
|
9
|
+
demonstrated: number;
|
|
10
|
+
touched: string[];
|
|
11
|
+
touchedUndemonstrated: Array<{
|
|
12
|
+
id: string;
|
|
13
|
+
reason: string;
|
|
14
|
+
}>;
|
|
15
|
+
untagged: Array<{
|
|
16
|
+
tag: string;
|
|
17
|
+
file: string;
|
|
18
|
+
line: number;
|
|
19
|
+
}>;
|
|
20
|
+
/** Absolute path of the full report. */
|
|
21
|
+
path: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function specTracePath(context: Pick<PipelineContext, 'backlogRoot' | 'runId'>): string;
|
|
24
|
+
/**
|
|
25
|
+
* Scans the specs and tests after a passing verification, binds the report to
|
|
26
|
+
* the receipt and writes `spec-trace.json`. Advisory and fail-open: any error
|
|
27
|
+
* yields `undefined` and never changes the verification outcome.
|
|
28
|
+
*/
|
|
29
|
+
export declare function recordSpecTrace(context: PipelineContext, change: string, receipt: Pick<VerificationReceipt, 'id' | 'commands'>, reportedTests?: readonly string[]): SpecTraceSummary | undefined;
|
|
30
|
+
/** The latest report of a run, or null when verification has not passed yet (or the file is unreadable). */
|
|
31
|
+
export declare function readSpecTraceReport(context: Pick<PipelineContext, 'backlogRoot' | 'runId'>): SpecTraceReport | null;
|
|
32
|
+
/** The acceptance finding that records the advisory trace next to the receipt. */
|
|
33
|
+
export declare function specTraceFinding(trace: SpecTraceSummary): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pipelineStateDirectory } from '../../pipeline/pipeline-state.js';
|
|
4
|
+
import { SPEC_TRACE_FILE, SPEC_TRACE_SCHEMA_VERSION, traceSpecs, traceSummary } from '../../pipeline/spec-trace.js';
|
|
5
|
+
import { commandReachesTest } from '../compact/test-reachability.js';
|
|
6
|
+
import { write } from './artifacts.js';
|
|
7
|
+
const LIST_LIMIT = 50;
|
|
8
|
+
export function specTracePath(context) {
|
|
9
|
+
return path.join(pipelineStateDirectory(context), SPEC_TRACE_FILE);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Scans the specs and tests after a passing verification, binds the report to
|
|
13
|
+
* the receipt and writes `spec-trace.json`. Advisory and fail-open: any error
|
|
14
|
+
* yields `undefined` and never changes the verification outcome.
|
|
15
|
+
*/
|
|
16
|
+
export function recordSpecTrace(context, change, receipt, reportedTests = []) {
|
|
17
|
+
try {
|
|
18
|
+
const single = context.repositories.length === 1 ? context.repositories[0] : undefined;
|
|
19
|
+
const testFiles = single ? reportedTests.map(file => path.isAbsolute(file) ? path.relative(single.path, file) : file).filter(file => file && !file.startsWith('..')).map(file => ({ repositoryId: single.id, file })) : [];
|
|
20
|
+
const report = traceSpecs({
|
|
21
|
+
specsRoot: context.artifactRoot, changes: [change],
|
|
22
|
+
repositories: context.repositories.map(repository => ({ id: repository.id, root: repository.path })),
|
|
23
|
+
testFiles, receipt: { id: receipt.id, commands: receipt.commands.map(command => {
|
|
24
|
+
// Receipts record the absolute working directory; reachability wants it relative to the repository.
|
|
25
|
+
const root = context.repositories.find(item => item.id === command.repositoryId)?.path;
|
|
26
|
+
const cwd = root && command.cwd ? path.relative(root, path.resolve(root, command.cwd)) : '';
|
|
27
|
+
return { repositoryId: command.repositoryId, command: command.command, args: command.args, ...(cwd && !cwd.startsWith('..') ? { cwd } : {}), exitCode: command.exitCode };
|
|
28
|
+
}) },
|
|
29
|
+
reaches: commandReachesTest,
|
|
30
|
+
});
|
|
31
|
+
const file = specTracePath(context);
|
|
32
|
+
write(file, JSON.stringify(report, null, 2) + '\n');
|
|
33
|
+
return summarize(report, file, context.repositories.length > 1);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function summarize(report, file, multiple) {
|
|
40
|
+
const reasons = new Map(report.undemonstrated.map(item => [item.id, item.reason]));
|
|
41
|
+
return {
|
|
42
|
+
schemaVersion: report.schemaVersion, receiptId: report.receiptId, summary: traceSummary(report), requirements: report.requirements, demonstrated: report.demonstrated.length,
|
|
43
|
+
touched: report.touched.slice(0, LIST_LIMIT), touchedUndemonstrated: report.touchedUndemonstrated.slice(0, LIST_LIMIT).map(id => ({ id, reason: reasons.get(id) ?? 'no-test' })),
|
|
44
|
+
untagged: report.untagged.slice(0, LIST_LIMIT).map(tag => ({ tag: tag.tag, file: (multiple ? tag.repositoryId + ':' : '') + tag.file, line: tag.line })),
|
|
45
|
+
path: file,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** The latest report of a run, or null when verification has not passed yet (or the file is unreadable). */
|
|
49
|
+
export function readSpecTraceReport(context) {
|
|
50
|
+
try {
|
|
51
|
+
const file = specTracePath(context);
|
|
52
|
+
if (!existsSync(file))
|
|
53
|
+
return null;
|
|
54
|
+
const value = JSON.parse(readFileSync(file, 'utf8'));
|
|
55
|
+
return value && typeof value === 'object' && value.schemaVersion === SPEC_TRACE_SCHEMA_VERSION ? value : null;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** The acceptance finding that records the advisory trace next to the receipt. */
|
|
62
|
+
export function specTraceFinding(trace) {
|
|
63
|
+
const touched = trace.touched.length ? `; touched by this change ${trace.touched.length - trace.touchedUndemonstrated.length}/${trace.touched.length}${trace.touchedUndemonstrated.length ? ' (not demonstrated: ' + trace.touchedUndemonstrated.map(item => item.id).join(', ') + ')' : ''}` : '';
|
|
64
|
+
return `Spec trace (advisory, receipt ${trace.receiptId ?? 'unknown'}): ${trace.summary}${touched}.`;
|
|
65
|
+
}
|
|
@@ -14,6 +14,8 @@ export interface ArchitectureRecord {
|
|
|
14
14
|
question?: string;
|
|
15
15
|
/** True when a low-confidence design proceeded on stated assumptions instead of asking. */
|
|
16
16
|
assumed?: boolean;
|
|
17
|
+
/** Ambiguities the architect reported before planning (see clarifications.ts). */
|
|
18
|
+
clarifications?: import('./clarifications.js').Clarification[];
|
|
17
19
|
}
|
|
18
20
|
export interface DeveloperRecord {
|
|
19
21
|
summary: string;
|
|
@@ -52,6 +54,8 @@ export interface VerificationRecord {
|
|
|
52
54
|
selfMutation?: import('../../pipeline/pipeline-state.js').VerificationSelfMutation;
|
|
53
55
|
/** Files verification regenerated that the host kept in the candidate; roles must not revert them. Sticky for the run. */
|
|
54
56
|
adoptedOutputs?: import('../../pipeline/pipeline-state.js').CandidateFileChange[];
|
|
57
|
+
/** Advisory requirement ↔ test trace of a passing verification (spec-trace.json), bound to `receiptId`. */
|
|
58
|
+
specTrace?: import('./spec-trace-record.js').SpecTraceSummary;
|
|
55
59
|
}
|
|
56
60
|
export interface ReviewRecord {
|
|
57
61
|
sessionId?: string;
|
|
@@ -62,6 +66,10 @@ export interface ReviewRecord {
|
|
|
62
66
|
score: number;
|
|
63
67
|
aspects: Record<string, number>;
|
|
64
68
|
candidateHash?: string;
|
|
69
|
+
/** Advisory risk brief (see review-brief.ts); never part of the approval gate. */
|
|
70
|
+
brief?: import('./review-brief.js').ReviewBrief;
|
|
71
|
+
/** Why a malformed brief was dropped after the repair turn. */
|
|
72
|
+
briefDiagnostic?: string;
|
|
65
73
|
}
|
|
66
74
|
export interface ArchiveRecord {
|
|
67
75
|
archivePath: string;
|
|
@@ -11,6 +11,13 @@ export * from './core-host.js';
|
|
|
11
11
|
export * from './graph/state.js';
|
|
12
12
|
export { coreNodes, developerRecord, buildAcceptanceReport, MAX_DEEPEN_PASSES, type CoreNodeDeps } from './graph/nodes.js';
|
|
13
13
|
export { DEFAULT_REVIEW_POLICY, REVIEW_ASPECTS, evaluateReview, resolveReviewPolicy, type ReviewAspect, type ReviewPolicy } from './graph/review-policy.js';
|
|
14
|
+
export { REVIEW_BRIEF_RISK_LIMIT, REVIEW_BRIEF_SCHEMA, REVIEW_BRIEF_SCHEMA_VERSION, parseReviewBrief, type ReviewBrief, type ReviewRisk } from './graph/review-brief.js';
|
|
15
|
+
export { REVIEW_BRIEF_FILE, readReviewBriefRecord, reviewBriefPath, type ReviewBriefRecord } from './graph/review-brief-record.js';
|
|
16
|
+
export { BASELINE_BLOCKER, BASELINE_FILE, BASELINE_SCHEMA_VERSION, baselinePath, blastRadiusFiles, detectBaseline, readBaselineRecord, type BaselineDetection, type BaselineRecord } from './graph/baseline.js';
|
|
17
|
+
export { CAPABILITY_MAP_PATH, CAPABILITY_MAP_SCHEMA_VERSION, readCapabilityMap, type CapabilityMap } from './graph/capability-map.js';
|
|
18
|
+
export { SPEC_TRACE_FILE, SPEC_TRACE_SCHEMA_VERSION, requirementSlug, requirementId, traceSpecs, touchedRequirements, type SpecTraceReport, type SpecTraceInput } from '../pipeline/spec-trace.js';
|
|
19
|
+
export { readSpecTraceReport, specTracePath, type SpecTraceSummary } from './graph/spec-trace-record.js';
|
|
20
|
+
export { CLARIFICATIONS_FILE, CLARIFICATIONS_SCHEMA, CLARIFICATIONS_SCHEMA_VERSION, CLARIFICATION_POLICIES, blockingClarifications, designSection, parseClarifications, readClarificationsRecord, clarificationsPath, type Clarification, type ClarificationPolicy, type ClarificationsRecord } from './graph/clarifications.js';
|
|
14
21
|
export { createRoleInvoker, type Accept, type InvokeOptions, type InvokeOutcome, type RoleInvoker, type RoleInvokerDeps } from './graph/roles.js';
|
|
15
22
|
export { parseAgentObject } from './graph/artifacts.js';
|
|
16
23
|
export { FileCheckpointSaver, type GraphStoreIO, type SerializedGraphStore } from './graph-checkpointer.js';
|
|
@@ -11,6 +11,13 @@ export * from './core-host.js';
|
|
|
11
11
|
export * from './graph/state.js';
|
|
12
12
|
export { coreNodes, developerRecord, buildAcceptanceReport, MAX_DEEPEN_PASSES } from './graph/nodes.js';
|
|
13
13
|
export { DEFAULT_REVIEW_POLICY, REVIEW_ASPECTS, evaluateReview, resolveReviewPolicy } from './graph/review-policy.js';
|
|
14
|
+
export { REVIEW_BRIEF_RISK_LIMIT, REVIEW_BRIEF_SCHEMA, REVIEW_BRIEF_SCHEMA_VERSION, parseReviewBrief } from './graph/review-brief.js';
|
|
15
|
+
export { REVIEW_BRIEF_FILE, readReviewBriefRecord, reviewBriefPath } from './graph/review-brief-record.js';
|
|
16
|
+
export { BASELINE_BLOCKER, BASELINE_FILE, BASELINE_SCHEMA_VERSION, baselinePath, blastRadiusFiles, detectBaseline, readBaselineRecord } from './graph/baseline.js';
|
|
17
|
+
export { CAPABILITY_MAP_PATH, CAPABILITY_MAP_SCHEMA_VERSION, readCapabilityMap } from './graph/capability-map.js';
|
|
18
|
+
export { SPEC_TRACE_FILE, SPEC_TRACE_SCHEMA_VERSION, requirementSlug, requirementId, traceSpecs, touchedRequirements } from '../pipeline/spec-trace.js';
|
|
19
|
+
export { readSpecTraceReport, specTracePath } from './graph/spec-trace-record.js';
|
|
20
|
+
export { CLARIFICATIONS_FILE, CLARIFICATIONS_SCHEMA, CLARIFICATIONS_SCHEMA_VERSION, CLARIFICATION_POLICIES, blockingClarifications, designSection, parseClarifications, readClarificationsRecord, clarificationsPath } from './graph/clarifications.js';
|
|
14
21
|
export { createRoleInvoker } from './graph/roles.js';
|
|
15
22
|
export { parseAgentObject } from './graph/artifacts.js';
|
|
16
23
|
export { FileCheckpointSaver } from './graph-checkpointer.js';
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type AgentRole, type CliProvider, type RoleDescriptor, type AgentAccess, type ArtifactAccess, type OpenSpecSkill } from './executor-types.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
export declare const OPENSPEC_VERSION = "1.
|
|
3
|
+
export declare const OPENSPEC_VERSION = "1.14.3";
|
|
4
4
|
export declare const ROLE_SKILLS: {
|
|
5
5
|
readonly architect: 'openspec-ff-change';
|
|
6
6
|
readonly developer: 'openspec-apply-change';
|
|
7
7
|
readonly reviewer: 'openspec-verify-change';
|
|
8
8
|
};
|
|
9
|
+
export declare const DEFAULT_PROJECT_CONFIG = "schema: spec-driven\n";
|
|
9
10
|
export interface OpenSpecRoleContext {
|
|
10
11
|
evidenceScope?: {
|
|
11
12
|
backlogRoot: string;
|
|
@@ -7,8 +7,11 @@ import path from 'node:path';
|
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { BUILTIN_ROLE_POLICY, isBuiltinRole } from './executor-types.js';
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
-
export const OPENSPEC_VERSION = '1.
|
|
10
|
+
export const OPENSPEC_VERSION = '1.14.3';
|
|
11
11
|
export const ROLE_SKILLS = { architect: 'openspec-ff-change', developer: 'openspec-apply-change', reviewer: 'openspec-verify-change' };
|
|
12
|
+
/** Where `openspec init --tools` writes each provider's skills (Codex and Kimi moved in 1.14). */
|
|
13
|
+
const OPENSPEC_SKILL_DIRECTORIES = { claude: '.claude', codex: '.agents', gemini: '.gemini', kimi: '.kimi-code' };
|
|
14
|
+
export const DEFAULT_PROJECT_CONFIG = 'schema: spec-driven\n';
|
|
12
15
|
/** Saved built-in contexts retain their original shape; new roles carry explicit policy. */
|
|
13
16
|
export function openSpecPolicy(context) {
|
|
14
17
|
const legacy = isBuiltinRole(context.role) ? BUILTIN_ROLE_POLICY[context.role] : undefined;
|
|
@@ -144,7 +147,7 @@ export function prepareOpenSpec(root, change, directory) {
|
|
|
144
147
|
});
|
|
145
148
|
for (const provider of ['claude', 'codex', 'gemini', 'kimi']) {
|
|
146
149
|
for (const name of Object.values(ROLE_SKILLS)) {
|
|
147
|
-
const content = readFileSync(path.join(target,
|
|
150
|
+
const content = readFileSync(path.join(target, OPENSPEC_SKILL_DIRECTORIES[provider], 'skills', name, 'SKILL.md'), 'utf8');
|
|
148
151
|
const destination = path.join(skillRoot, provider, name);
|
|
149
152
|
mkdirSync(destination, { recursive: true });
|
|
150
153
|
writeFileSync(path.join(destination, 'SKILL.md'), content, { mode: 0o600 });
|
|
@@ -163,8 +166,11 @@ export function prepareOpenSpec(root, change, directory) {
|
|
|
163
166
|
}
|
|
164
167
|
if (existsSync(path.join(root, 'openspec/schemas/spec-driven')))
|
|
165
168
|
throw new Error('Custom spec-driven schema overrides are not supported by this runtime');
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
// Same precedence as OpenSpec. `new change` scaffolds DEFAULT_PROJECT_CONFIG when no config exists;
|
|
170
|
+
// it is equivalent to none, so the architect's first write does not change the run's identity.
|
|
171
|
+
const config = ['openspec/config.yaml', 'openspec/config.yml'].map(file => artifactPath(root, file)).find(file => existsSync(file));
|
|
172
|
+
const projectConfig = config ? readFileSync(config, 'utf8') : undefined;
|
|
173
|
+
identity.projectConfig = projectConfig === undefined || projectConfig === DEFAULT_PROJECT_CONFIG ? 'absent' : hash(projectConfig);
|
|
168
174
|
return { cli, skillRoot, identity };
|
|
169
175
|
}
|
|
170
176
|
export function roleOpenSpecContext(prepared, root, change, directory, roleOrDescriptor, provider) {
|
|
@@ -349,7 +355,7 @@ export const OPENSPEC_TOOL_DEFINITION = { type: 'function', function: { name: 'o
|
|
|
349
355
|
export function openSpecPrompt(context) {
|
|
350
356
|
if (!isBuiltinRole(context.role))
|
|
351
357
|
return `\n## Official OpenSpec workflow binding\nExecute ${openSpecSkill(context)} for change ${context.change}. Load action=load_skill using openspec_workflow and follow the pinned official skill. Use scoped tools for status, instructions, validation and permitted artifact writes. Workspace access: ${openSpecPolicy(context).access}; artifacts: ${openSpecPolicy(context).artifacts}. Read required artifact instructions before writing. Never archive or coordinate another role.\n`;
|
|
352
|
-
return `\n## Official OpenSpec workflow binding\nExecute ${openSpecSkill(context)} for change ${context.change}. This headless transport loads the official, version-pinned skill through the openspec_workflow tool (MCP server specrails_openspec, tool workflow on CLI providers). This is explicit skill-document adaptation, not a native slash command.\nFirst call action=load_skill and follow the returned complete official skill. Bind its openspec CLI commands to this tool: new change -> action=new; status -> status; instructions <artifact> -> instructions with artifact; validate -> validate. Author each artifact yourself using write_artifact with its change-relative path and content; read the official instructions first. Do not reproduce templates from memory or return the artifacts in final JSON. The tool fixes the change identity and runs OpenSpec ${OPENSPEC_VERSION}.\nRead source/dependencies with your normal read tools. For this frozen scope, change selection is already answered. Bind AskUserQuestion to a low-confidence final result with question (architect), or a reported blocking issue (other roles); LangGraph asks the requester. Bind TodoWrite to concise progress narration and, for developer, persist the current handoff through action=write_progress with progress {summary, completedTasks, nextTasks, checks: [{command, outcome}], blockers}. Save after each completed task or changed blocker, at most 8 KB. This host journal is separate from frozen OpenSpec artifacts; never write runtime files directly. Developer/reviewer receive savedProgress from load_skill and can refresh with read_progress. Reconcile this advisory history with current files; old test outcomes never replace required host verification. Do not invoke another role, apply/archive from architect, or archive from developer/reviewer. Return the Specrails JSON report after the official workflow. For developer and reviewer, load_skill already executes status and instructions apply and returns their real outputs in planning; read planning.apply.contextFiles and perform the rest of the official skill. You may refresh instructions apply when needed. Verify is a skill, not an instructions artifact.\n`;
|
|
358
|
+
return `\n## Official OpenSpec workflow binding\nExecute ${openSpecSkill(context)} for change ${context.change}. This headless transport loads the official, version-pinned skill through the openspec_workflow tool (MCP server specrails_openspec, tool workflow on CLI providers). This is explicit skill-document adaptation, not a native slash command.\nFirst call action=load_skill and follow the returned complete official skill. Bind its openspec CLI commands to this tool: new change -> action=new; status -> status; instructions <artifact> -> instructions with artifact; validate -> validate. Author each artifact yourself using write_artifact with its change-relative path and content; read the official instructions first. Do not reproduce templates from memory or return the artifacts in final JSON. The tool fixes the change identity and runs OpenSpec ${OPENSPEC_VERSION}.\nRead source/dependencies with your normal read tools. For this frozen scope, change selection is already answered, and so are the skill's store selection and project check: no store is selected (never pass --store or run store commands), and the host has admitted this repository as the OpenSpec project, so action=new may create its repo-local openspec/ root; do not run openspec list to check for one. Bind AskUserQuestion to a low-confidence final result with question (architect), or a reported blocking issue (other roles); LangGraph asks the requester. Bind TodoWrite to concise progress narration and, for developer, persist the current handoff through action=write_progress with progress {summary, completedTasks, nextTasks, checks: [{command, outcome}], blockers}. Save after each completed task or changed blocker, at most 8 KB. This host journal is separate from frozen OpenSpec artifacts; never write runtime files directly. Developer/reviewer receive savedProgress from load_skill and can refresh with read_progress. Reconcile this advisory history with current files; old test outcomes never replace required host verification. Do not invoke another role, apply/archive from architect, or archive from developer/reviewer. Return the Specrails JSON report after the official workflow. For developer and reviewer, load_skill already executes status and instructions apply and returns their real outputs in planning; read planning.apply.contextFiles and perform the rest of the official skill. You may refresh instructions apply when needed. Verify is a skill, not an instructions artifact.\n`;
|
|
353
359
|
}
|
|
354
360
|
export function writeOpenSpecBridge(context, directory) {
|
|
355
361
|
const file = path.join(directory, 'openspec-context.json');
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { PipelineContext } from '../pipeline/pipeline-state.js';
|
|
2
|
+
import type { ExecutorRegistry } from './executors.js';
|
|
3
|
+
import type { AgentExecutor, AgentUsage, RuntimeConfig } from './executor-types.js';
|
|
4
|
+
export declare const RULES_INVENTORY_VERSION = 1;
|
|
5
|
+
export declare const RULES_FILE = ".specrails/rules.json";
|
|
6
|
+
export declare const RULE_SOURCE_FILES: readonly ['CLAUDE.md', 'AGENTS.md', 'CONTRIBUTING.md', '.github/CONTRIBUTING.md', 'docs/CONTRIBUTING.md'];
|
|
7
|
+
export declare const GATE_KINDS: readonly ['lint', 'format', 'typecheck', 'test', 'architecture', 'hook', 'ci', 'spec'];
|
|
8
|
+
export declare const RULE_STATUSES: readonly ['enforcedBy', 'automatable', 'humanOnly', 'unclassified'];
|
|
9
|
+
export type GateKind = typeof GATE_KINDS[number];
|
|
10
|
+
export type RuleStatus = typeof RULE_STATUSES[number];
|
|
11
|
+
export interface RulesGate {
|
|
12
|
+
id: string;
|
|
13
|
+
kind: GateKind;
|
|
14
|
+
/** Shell-free command line a host can run from the repository root. */
|
|
15
|
+
command: string;
|
|
16
|
+
/** Repository-relative file that revealed the gate. */
|
|
17
|
+
source: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ProjectRule {
|
|
20
|
+
/** Stable across line moves: hash of the source file and the normalized statement. */
|
|
21
|
+
id: string;
|
|
22
|
+
text: string;
|
|
23
|
+
/** `<repository-relative file>:<line>` of the statement's first line. */
|
|
24
|
+
source: string;
|
|
25
|
+
status: RuleStatus;
|
|
26
|
+
/** With `enforcedBy`: the gate id (or the named test/check) that already enforces the rule. */
|
|
27
|
+
enforcedBy?: string;
|
|
28
|
+
/** With `automatable`: the check that would enforce it. */
|
|
29
|
+
proposedCheck?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface RulesClassification {
|
|
32
|
+
status: 'classified' | 'cached' | 'offline' | 'unavailable' | 'failed' | 'empty';
|
|
33
|
+
provider?: string;
|
|
34
|
+
model?: string;
|
|
35
|
+
reason?: string;
|
|
36
|
+
classifiedAt?: string;
|
|
37
|
+
usage?: AgentUsage;
|
|
38
|
+
}
|
|
39
|
+
export interface RulesInventory {
|
|
40
|
+
schemaVersion: 1;
|
|
41
|
+
generatedAt: string;
|
|
42
|
+
gates: RulesGate[];
|
|
43
|
+
rules: ProjectRule[];
|
|
44
|
+
classification: RulesClassification;
|
|
45
|
+
}
|
|
46
|
+
/** JSON with comments and trailing commas (tsconfig, biome.jsonc). */
|
|
47
|
+
export declare function parseJsonc(text: string): unknown;
|
|
48
|
+
/** `run:` commands of a GitHub workflow (single line, literal `|` and folded `>` block scalars). */
|
|
49
|
+
export declare function workflowRunCommands(text: string): string[];
|
|
50
|
+
/** Deterministic gate discovery for one repository root. */
|
|
51
|
+
export declare function discoverGates(root: string): RulesGate[];
|
|
52
|
+
export declare function ruleId(file: string, text: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Rule statements of one instruction file: list items (with their wrapped
|
|
55
|
+
* continuation lines) and strongly normative paragraph sentences, outside code
|
|
56
|
+
* fences, tables and Specrails-managed blocks.
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseRuleStatements(text: string, file: string): Array<{
|
|
59
|
+
text: string;
|
|
60
|
+
source: string;
|
|
61
|
+
}>;
|
|
62
|
+
/** Every rule statement of the instruction files present in `root`, deduplicated, with stable ids. */
|
|
63
|
+
export declare function collectRuleStatements(root: string): ProjectRule[];
|
|
64
|
+
export declare const RULES_CLASSIFICATION_SCHEMA: {
|
|
65
|
+
readonly type: 'object';
|
|
66
|
+
readonly additionalProperties: false;
|
|
67
|
+
readonly required: readonly ['rules'];
|
|
68
|
+
readonly properties: {
|
|
69
|
+
readonly rules: {
|
|
70
|
+
readonly type: 'array';
|
|
71
|
+
readonly items: {
|
|
72
|
+
readonly type: 'object';
|
|
73
|
+
readonly additionalProperties: false;
|
|
74
|
+
readonly required: readonly ['id', 'status'];
|
|
75
|
+
readonly properties: {
|
|
76
|
+
readonly id: {
|
|
77
|
+
readonly type: 'string';
|
|
78
|
+
};
|
|
79
|
+
readonly status: {
|
|
80
|
+
readonly enum: readonly ['enforcedBy', 'automatable', 'humanOnly'];
|
|
81
|
+
};
|
|
82
|
+
readonly enforcedBy: {
|
|
83
|
+
readonly type: 'string';
|
|
84
|
+
};
|
|
85
|
+
readonly proposedCheck: {
|
|
86
|
+
readonly type: 'string';
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
export declare function classificationPrompt(gates: readonly RulesGate[], rules: readonly ProjectRule[]): string;
|
|
94
|
+
/** Applies a model reply to the parsed rules: only known ids and statuses; text and source never change. */
|
|
95
|
+
export declare function applyClassification(rules: readonly ProjectRule[], reply: Record<string, unknown>): {
|
|
96
|
+
rules: ProjectRule[];
|
|
97
|
+
classified: number;
|
|
98
|
+
};
|
|
99
|
+
export declare function readRulesInventory(file: string): RulesInventory | undefined;
|
|
100
|
+
export interface RulesInventoryOptions {
|
|
101
|
+
/** Repository whose gates and instruction files are inventoried. */
|
|
102
|
+
root: string;
|
|
103
|
+
/** Directory holding `.specrails/` (rules.json and agent-runtime.json); defaults to `root`. */
|
|
104
|
+
artifactRoot?: string;
|
|
105
|
+
/** false: deterministic only (gates + rules, unclassified unless cached). */
|
|
106
|
+
classify?: boolean;
|
|
107
|
+
/** Ignore cached classifications and classify every rule again. */
|
|
108
|
+
reclassify?: boolean;
|
|
109
|
+
/** Runtime configuration; defaults to `<artifactRoot>/.specrails/agent-runtime.json`. */
|
|
110
|
+
config?: RuntimeConfig;
|
|
111
|
+
/** Programmatic executors (tests, hosts) replacing configured ones. */
|
|
112
|
+
executors?: Record<string, AgentExecutor>;
|
|
113
|
+
registry?: ExecutorRegistry;
|
|
114
|
+
timeoutMs?: number;
|
|
115
|
+
signal?: AbortSignal;
|
|
116
|
+
now?: () => Date;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Builds and writes `.specrails/rules.json`. Rules whose statement did not
|
|
120
|
+
* change keep their previous classification, so a re-run (and `init`) only
|
|
121
|
+
* classifies what is new; the classification never blocks the inventory.
|
|
122
|
+
*/
|
|
123
|
+
export declare function runRulesInventory(options: RulesInventoryOptions): Promise<{
|
|
124
|
+
file: string;
|
|
125
|
+
inventory: RulesInventory;
|
|
126
|
+
}>;
|
|
127
|
+
/** What the runtime hands the roles: the human-only rules and the discovered gates. */
|
|
128
|
+
export interface ProjectRulesContext {
|
|
129
|
+
humanOnly: Array<Pick<ProjectRule, 'text' | 'source'>>;
|
|
130
|
+
gates: Array<Pick<RulesGate, 'id' | 'kind' | 'command'>>;
|
|
131
|
+
}
|
|
132
|
+
/** Reads `.specrails/rules.json` next to the run's backlog, else in the artifact repository. */
|
|
133
|
+
export declare function projectRulesContext(context: Pick<PipelineContext, 'backlogRoot' | 'artifactRoot'>): ProjectRulesContext | undefined;
|
|
134
|
+
/** `specrails-core rules inventory [--root <dir>] [--artifact-root <dir>] [--offline] [--reclassify] [--json]`. */
|
|
135
|
+
export declare function runRulesCommand(flags: Record<string, string | boolean>, positionals: string[], io?: {
|
|
136
|
+
stdout?: (text: string) => void;
|
|
137
|
+
stderr?: (text: string) => void;
|
|
138
|
+
executors?: Record<string, AgentExecutor>;
|
|
139
|
+
}): Promise<number>;
|