@planu/cli 4.11.0 → 4.11.2
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 +16 -0
- package/dist/engine/cascade-hooks/registry.js +0 -2
- package/dist/engine/challenge-scenarios-loader.d.ts +3 -3
- package/dist/engine/compliance-injector.d.ts +3 -3
- package/dist/engine/dashboard/kanban-logic.d.ts +1 -1
- package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +1 -1
- package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +1 -1
- package/dist/engine/elicitation/answer-extractor.d.ts +1 -4
- package/dist/engine/elicitation/answer-extractor.js +197 -68
- package/dist/engine/elicitation/decision-gap-detector.js +59 -79
- package/dist/engine/elicitation/index.d.ts +0 -1
- package/dist/engine/elicitation/index.js +0 -1
- package/dist/engine/elicitation/question-grounding-gate.js +83 -34
- package/dist/engine/evidence-gates/lifecycle-gate.js +27 -14
- package/dist/engine/evidence-index/index-builder.js +7 -2
- package/dist/engine/frontmatter-sha/frozen-fields.d.ts +1 -1
- package/dist/engine/handoff-artifacts/schemas/red-tests-handoff.d.ts +2 -2
- package/dist/engine/handoff-artifacts/schemas.d.ts +22 -22
- package/dist/engine/human-summary.js +3 -3
- package/dist/engine/implementation-contract/common.d.ts +1 -1
- package/dist/engine/keyword-matcher/stopwords.d.ts +2 -2
- package/dist/engine/provider-adapters/adapters/claude.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/gemini.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/gpt4.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/markdown.d.ts +1 -1
- package/dist/engine/safety/atomic-write-file.d.ts +1 -1
- package/dist/engine/safety/cross-process-lock.d.ts +1 -1
- package/dist/engine/safety/path-sanitizer.d.ts +1 -1
- package/dist/engine/security-analyzer/profile-detector.js +48 -16
- package/dist/engine/spec-format/copula-blacklist.d.ts +2 -2
- package/dist/engine/spec-format/metadata-value-policy.d.ts +20 -20
- package/dist/engine/text-signal-boundaries.js +45 -12
- package/dist/engine/universal-rules/rules/planu-modes.js +0 -2
- package/dist/engine/well-known/manifest-schema.d.ts +33 -33
- package/dist/engine/workers/schema.d.ts +3 -3
- package/dist/hosts/claude-code/ux/mcp-resources.d.ts +4 -4
- package/dist/hosts/codex/rest-shim.d.ts +1 -1
- package/dist/native/lightweight-command-catalog.d.ts +30 -30
- package/dist/server/openapi-spec.d.ts +132 -132
- package/dist/tools/challenge-spec/event-challenge-scenarios.js +8 -27
- package/dist/tools/challenge-spec/resilience-challenge-scenarios-a.js +16 -32
- package/dist/tools/challenge-spec/resilience-challenge-scenarios-b.js +15 -20
- package/dist/tools/challenge-spec/scenario-collector.d.ts +13 -0
- package/dist/tools/challenge-spec/scenario-collector.js +86 -0
- package/dist/tools/challenge-spec/scenarios-data.js +10 -9
- package/dist/tools/challenge-spec/scenarios-failure.js +38 -54
- package/dist/tools/challenge-spec/scenarios-scale.js +14 -15
- package/dist/tools/challenge-spec/scenarios-security.d.ts +1 -1
- package/dist/tools/challenge-spec/scenarios-security.js +18 -11
- package/dist/tools/challenge-spec/scenarios-utils.d.ts +31 -5
- package/dist/tools/challenge-spec/scenarios-utils.js +102 -6
- package/dist/tools/challenge-spec/security-challenge-scenarios.d.ts +1 -1
- package/dist/tools/challenge-spec/security-challenge-scenarios.js +20 -25
- package/dist/tools/challenge-spec.js +39 -118
- package/dist/tools/compliance-gate-handler.d.ts +1 -1
- package/dist/tools/create-spec.js +4 -24
- package/dist/tools/delete-first/suggest-deletions.d.ts +1 -1
- package/dist/tools/generate-batch-script.d.ts +2 -2
- package/dist/tools/init-project/rules-generator.js +0 -1
- package/dist/tools/oauth-handler.d.ts +1 -1
- package/dist/tools/register-sdd-tools.d.ts +1 -1
- package/dist/tools/render-spec-for-provider.d.ts +2 -2
- package/dist/tools/scan-orphan-spec-refs.d.ts +6 -6
- package/dist/tools/schemas/agents.d.ts +7 -7
- package/dist/tools/schemas/analysis.d.ts +11 -11
- package/dist/tools/schemas/delete.d.ts +5 -5
- package/dist/tools/schemas/github.d.ts +4 -4
- package/dist/tools/schemas/ide-config.d.ts +12 -12
- package/dist/tools/schemas/infra.d.ts +9 -9
- package/dist/tools/schemas/lifecycle.d.ts +50 -50
- package/dist/tools/schemas/llm-provider-schemas.d.ts +7 -7
- package/dist/tools/schemas/plugins-schemas.d.ts +12 -12
- package/dist/tools/schemas/project-graph.d.ts +6 -6
- package/dist/tools/schemas/rbac.d.ts +2 -2
- package/dist/tools/schemas/registry.d.ts +3 -3
- package/dist/tools/schemas/runtime-security.d.ts +2 -2
- package/dist/tools/schemas/session.d.ts +2 -2
- package/dist/tools/schemas/spec.d.ts +15 -15
- package/dist/tools/schemas/token-intelligence.d.ts +6 -6
- package/dist/tools/schemas/token-optimization.d.ts +2 -2
- package/dist/tools/schemas/workers-schema.d.ts +4 -4
- package/dist/tools/skill-bootstrap-handler.d.ts +7 -7
- package/dist/tools/tool-registry/deprecated-stubs.d.ts +1 -1
- package/dist/tools/triage-request.d.ts +3 -3
- package/dist/tools/update-status/file-sync.d.ts +1 -1
- package/dist/tools/update-status/file-sync.js +10 -45
- package/dist/tools/update-status/index.js +28 -27
- package/dist/tools/update-status/response-builder.d.ts +1 -1
- package/dist/tools/update-status/response-builder.js +11 -19
- package/dist/tools/update-status/transition-guard.js +1 -1
- package/dist/tools/update-status-actions.d.ts +0 -1
- package/dist/tools/update-status-actions.js +10 -26
- package/dist/tools/update-status-convention-gate.d.ts +1 -6
- package/dist/tools/update-status-convention-gate.js +9 -33
- package/dist/types/docs.d.ts +0 -4
- package/dist/types/env.d.ts +0 -4
- package/dist/types/estimation.d.ts +0 -2
- package/dist/types/multi-teammate-review.d.ts +4 -4
- package/dist/types/plugin-install.d.ts +7 -7
- package/package.json +21 -18
- package/planu-native.json +1 -1
- package/planu-plugin.json +1 -1
- package/src/i18n/messages/en.json +0 -72
- package/src/i18n/messages/es.json +0 -72
- package/src/i18n/messages/pt.json +0 -72
- package/dist/config/elicitation-dimensions.json +0 -51
- package/dist/engine/actuals-estimator.d.ts +0 -14
- package/dist/engine/actuals-estimator.js +0 -64
- package/dist/engine/cascade-hooks/hooks/living-specs.hook.d.ts +0 -3
- package/dist/engine/cascade-hooks/hooks/living-specs.hook.js +0 -19
- package/dist/engine/elicitation/option-builder.d.ts +0 -9
- package/dist/engine/elicitation/option-builder.js +0 -85
- package/dist/engine/progress-writer.d.ts +0 -10
- package/dist/engine/progress-writer.js +0 -63
- package/dist/tools/update-status/mode-hints.d.ts +0 -4
- package/dist/tools/update-status/mode-hints.js +0 -24
|
@@ -6,49 +6,98 @@ const GENERIC_QUESTION_PATTERNS = [
|
|
|
6
6
|
/^what data is needed\??$/i,
|
|
7
7
|
/^what are the requirements\??$/i,
|
|
8
8
|
];
|
|
9
|
+
const REQUIRED_EVIDENCE = {
|
|
10
|
+
permission: ['capability:permission'],
|
|
11
|
+
provider: ['domain:billing'],
|
|
12
|
+
'billing-model': ['domain:billing'],
|
|
13
|
+
data: ['domain:data'],
|
|
14
|
+
failure: ['integration:'],
|
|
15
|
+
behavior: ['request:short-ambiguous'],
|
|
16
|
+
scope: ['request:short-ambiguous'],
|
|
17
|
+
};
|
|
18
|
+
const OPTION_CONCEPTS = [
|
|
19
|
+
[/\b(payment|billing|checkout|subscription|invoice|provider|vendor)\b/i, ['domain:billing']],
|
|
20
|
+
[/\b(database|schema|table|sql|persistence)\b/i, ['domain:data']],
|
|
21
|
+
[/\b(admin|role|auth|authorization|permission|access control)\b/i, ['capability:permission']],
|
|
22
|
+
[/\b(retry|backoff|queue|queued)\b/i, ['capability:retry']],
|
|
23
|
+
[/\b(docker|kubernetes|cloud|serverless)\b/i, ['capability:infrastructure']],
|
|
24
|
+
];
|
|
25
|
+
const NEUTRAL_TITLE_WORDS = new Set([
|
|
26
|
+
'access',
|
|
27
|
+
'apply',
|
|
28
|
+
'behavior',
|
|
29
|
+
'bounded',
|
|
30
|
+
'current',
|
|
31
|
+
'data',
|
|
32
|
+
'define',
|
|
33
|
+
'describe',
|
|
34
|
+
'existing',
|
|
35
|
+
'failure',
|
|
36
|
+
'in',
|
|
37
|
+
'include',
|
|
38
|
+
'keep',
|
|
39
|
+
'limit',
|
|
40
|
+
'maintenance',
|
|
41
|
+
'new',
|
|
42
|
+
'no',
|
|
43
|
+
'out',
|
|
44
|
+
'project',
|
|
45
|
+
'provider',
|
|
46
|
+
'request',
|
|
47
|
+
'scope',
|
|
48
|
+
'selection',
|
|
49
|
+
'the',
|
|
50
|
+
'use',
|
|
51
|
+
]);
|
|
9
52
|
export function validateQuestionGrounding(question, context) {
|
|
10
53
|
const text = question.question.trim();
|
|
11
54
|
if (GENERIC_QUESTION_PATTERNS.some((pattern) => pattern.test(text))) {
|
|
12
55
|
return { passed: false, reason: 'Question is a generic reusable prompt.' };
|
|
13
56
|
}
|
|
57
|
+
const requiredMarkers = REQUIRED_EVIDENCE[context.gap.kind] ?? [];
|
|
58
|
+
if (!requiredMarkers.some((marker) => hasEvidenceMarker(context.gap.evidence, marker))) {
|
|
59
|
+
return { passed: false, reason: 'Gap lacks affirmed evidence for this decision kind.' };
|
|
60
|
+
}
|
|
61
|
+
const groundingPhrases = context.gap.evidence
|
|
62
|
+
.filter((item) => /^(?:action|object|integration|project):/i.test(item))
|
|
63
|
+
.map((item) => item.slice(item.indexOf(':') + 1).toLowerCase())
|
|
64
|
+
.filter((item) => item.length > 1);
|
|
14
65
|
const lowerQuestion = text.toLowerCase();
|
|
15
|
-
const
|
|
16
|
-
const evidenceTokens = tokenSet(lowerEvidence);
|
|
17
|
-
const hasRequestEvidence = Array.from(evidenceTokens).some((token) => token.length >= 3 && lowerQuestion.includes(token));
|
|
66
|
+
const hasRequestEvidence = groundingPhrases.some((phrase) => lowerQuestion.includes(phrase));
|
|
18
67
|
if (!hasRequestEvidence) {
|
|
19
|
-
return {
|
|
20
|
-
passed: false,
|
|
21
|
-
reason: 'Question does not include evidence from the request or detected gap.',
|
|
22
|
-
};
|
|
68
|
+
return { passed: false, reason: 'Question does not include concrete request evidence.' };
|
|
23
69
|
}
|
|
24
|
-
if (!/\bchanges?\b|\
|
|
25
|
-
return {
|
|
26
|
-
passed: false,
|
|
27
|
-
reason: 'Question does not explain why the answer matters for the spec.',
|
|
28
|
-
};
|
|
70
|
+
if (!/\bchanges?\b|\baffects?\b|\bbecause\b|\bbecomes?\b/i.test(text)) {
|
|
71
|
+
return { passed: false, reason: 'Question does not explain why the answer matters.' };
|
|
29
72
|
}
|
|
30
|
-
|
|
73
|
+
const optionFailure = validateOptions(question, context);
|
|
74
|
+
return optionFailure ?? { passed: true };
|
|
31
75
|
}
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
-
|
|
76
|
+
function validateOptions(question, context) {
|
|
77
|
+
const evidenceText = `${context.requestText} ${context.gap.evidence.join(' ')}`.toLowerCase();
|
|
78
|
+
for (const option of question.options) {
|
|
79
|
+
const optionText = `${option.label} ${option.description}`;
|
|
80
|
+
for (const [pattern, markers] of OPTION_CONCEPTS) {
|
|
81
|
+
if (pattern.test(optionText) &&
|
|
82
|
+
!markers.some((marker) => hasEvidenceMarker(context.gap.evidence, marker))) {
|
|
83
|
+
return { passed: false, reason: 'Option introduces a domain concept without evidence.' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const properNames = optionText.match(/\b(?:[A-Z]{2,}|[A-Z][a-z]+(?:[A-Z][A-Za-z]+)?)\b/g) ?? [];
|
|
87
|
+
const unsupported = properNames.find((name) => {
|
|
88
|
+
const lower = name.toLowerCase();
|
|
89
|
+
return !NEUTRAL_TITLE_WORDS.has(lower) && !evidenceText.includes(lower);
|
|
90
|
+
});
|
|
91
|
+
if (unsupported !== undefined) {
|
|
92
|
+
return {
|
|
93
|
+
passed: false,
|
|
94
|
+
reason: `Option introduces unsupported named candidate: ${unsupported}.`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function hasEvidenceMarker(evidence, marker) {
|
|
101
|
+
return evidence.some((item) => item.toLowerCase().startsWith(marker));
|
|
37
102
|
}
|
|
38
|
-
const STOP_WORDS = new Set([
|
|
39
|
-
'the',
|
|
40
|
-
'and',
|
|
41
|
-
'for',
|
|
42
|
-
'this',
|
|
43
|
-
'that',
|
|
44
|
-
'with',
|
|
45
|
-
'from',
|
|
46
|
-
'what',
|
|
47
|
-
'when',
|
|
48
|
-
'where',
|
|
49
|
-
'should',
|
|
50
|
-
'action',
|
|
51
|
-
'object',
|
|
52
|
-
'risk',
|
|
53
|
-
]);
|
|
54
103
|
//# sourceMappingURL=question-grounding-gate.js.map
|
|
@@ -1,26 +1,39 @@
|
|
|
1
|
+
import { hasAnyAffirmedMatch } from '../text-signal-boundaries.js';
|
|
1
2
|
const CONTRACT_HINTS = {
|
|
2
|
-
api:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
api: [
|
|
4
|
+
/\b(?:public|external|rest|http)[ -]?api\b/i,
|
|
5
|
+
/\bapi\s+(?:endpoint|route|request|response|contract)\b/i,
|
|
6
|
+
/\b(?:rest|http)\s+(?:endpoint|route|contract)\b/i,
|
|
7
|
+
/\bopenapi\b/i,
|
|
8
|
+
],
|
|
9
|
+
graphql: [/\b(?:graphql|gql)\s+(?:api|schema|resolver|contract)\b/i, /\bschema\.graphql\b/i],
|
|
10
|
+
event: [
|
|
11
|
+
/\b(?:event|message)\s+(?:contract|schema|producer|consumer|handler|queue|topic)\b/i,
|
|
12
|
+
/\b(?:message\s+broker|kafka|rabbitmq|pubsub|webhook\s+(?:delivery|contract|payload))\b/i,
|
|
13
|
+
],
|
|
14
|
+
ui: [/\b(?:ui|component|screen|design)\s+contract\b/i],
|
|
15
|
+
mcp: [
|
|
16
|
+
/\bmcp\s+(?:tool|contract|server|protocol)\b/i,
|
|
17
|
+
/\b(?:tool\s+contract|model\s+context\s+protocol|registerTool|server\s+tool)\b/i,
|
|
18
|
+
],
|
|
7
19
|
};
|
|
8
20
|
function isNonTrivial(spec) {
|
|
9
21
|
return spec.scope !== 'trivial';
|
|
10
22
|
}
|
|
11
23
|
function normalizeCriterion(value) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
const withoutCheckbox = value.replace(/^-\s*\[[ x]\]\s*/i, '').trim();
|
|
25
|
+
const criterionOnly = /^(AC\d+)\s*[:.)-]?\s*$/i.exec(withoutCheckbox)?.[1];
|
|
26
|
+
if (criterionOnly) {
|
|
27
|
+
return criterionOnly.toLowerCase();
|
|
28
|
+
}
|
|
29
|
+
return withoutCheckbox
|
|
30
|
+
.replace(/^AC\d+\s*[:.)-]?\s*/i, '')
|
|
15
31
|
.replace(/\s+/g, ' ')
|
|
16
32
|
.trim()
|
|
17
33
|
.toLowerCase();
|
|
18
34
|
}
|
|
19
35
|
function criterionAliases(value, index) {
|
|
20
|
-
const aliases = new Set([
|
|
21
|
-
normalizeCriterion(value),
|
|
22
|
-
normalizeCriterion(`AC${String(index + 1)}`),
|
|
23
|
-
]);
|
|
36
|
+
const aliases = new Set([normalizeCriterion(value), `ac${String(index + 1)}`]);
|
|
24
37
|
const explicit = /^(AC\d+)\b/i.exec(value)?.[1];
|
|
25
38
|
if (explicit) {
|
|
26
39
|
aliases.add(normalizeCriterion(explicit));
|
|
@@ -33,8 +46,8 @@ function hasText(value) {
|
|
|
33
46
|
export function inferRequiredContractEvidence(spec, criteria) {
|
|
34
47
|
const text = `${spec.title} ${spec.tags.join(' ')} ${criteria.join(' ')}`;
|
|
35
48
|
const required = [];
|
|
36
|
-
for (const [kind,
|
|
37
|
-
if (
|
|
49
|
+
for (const [kind, patterns] of Object.entries(CONTRACT_HINTS)) {
|
|
50
|
+
if (hasAnyAffirmedMatch(text, [...patterns])) {
|
|
38
51
|
required.push(kind);
|
|
39
52
|
}
|
|
40
53
|
}
|
|
@@ -155,8 +155,13 @@ function record(kind, value, status, source, reason) {
|
|
|
155
155
|
return { kind, value, status, source, ...(reason ? { reason } : {}) };
|
|
156
156
|
}
|
|
157
157
|
function normalizeCriterion(value) {
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
const withoutCheckbox = value.replace(/^-\s*\[[ x]\]\s*/i, '').trim();
|
|
159
|
+
const criterionOnly = /^(AC\d+)\s*[:.)-]?\s*$/i.exec(withoutCheckbox)?.[1];
|
|
160
|
+
if (criterionOnly) {
|
|
161
|
+
return criterionOnly.toLowerCase();
|
|
162
|
+
}
|
|
163
|
+
return withoutCheckbox
|
|
164
|
+
.replace(/^AC\d+\s*[:.)-]?\s*/i, '')
|
|
160
165
|
.replace(/\s+/g, ' ')
|
|
161
166
|
.trim()
|
|
162
167
|
.toLowerCase();
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Frontmatter fields that become immutable when a spec transitions to a terminal state
|
|
3
3
|
* (done or discarded). Any change to these fields after the SHA freeze is a tamper signal.
|
|
4
4
|
*/
|
|
5
|
-
export declare const FROZEN_FIELDS: readonly [
|
|
5
|
+
export declare const FROZEN_FIELDS: readonly ['status', 'scenarios', 'spec_version', 'id', 'type', 'target'];
|
|
6
6
|
export type FrozenField = (typeof FROZEN_FIELDS)[number];
|
|
7
7
|
//# sourceMappingURL=frozen-fields.d.ts.map
|
|
@@ -8,10 +8,10 @@ export declare const RedTestsHandoffSchema: z.ZodObject<{
|
|
|
8
8
|
testFiles: z.ZodArray<z.ZodString>;
|
|
9
9
|
testFramework: z.ZodEnum<{
|
|
10
10
|
jest: "jest";
|
|
11
|
-
|
|
11
|
+
playwright: "playwright";
|
|
12
12
|
pytest: "pytest";
|
|
13
13
|
rspec: "rspec";
|
|
14
|
-
|
|
14
|
+
vitest: "vitest";
|
|
15
15
|
}>;
|
|
16
16
|
expectedFailures: z.ZodArray<z.ZodString>;
|
|
17
17
|
authoredBy: z.ZodString;
|
|
@@ -5,11 +5,11 @@ export declare const SchemaVersion: z.ZodString;
|
|
|
5
5
|
export declare const IntakeV1Schema: z.ZodObject<{
|
|
6
6
|
schema_version: z.ZodString;
|
|
7
7
|
kind: z.ZodEnum<{
|
|
8
|
-
idea: "idea";
|
|
9
|
-
"quick-fix": "quick-fix";
|
|
10
8
|
"bug-spec": "bug-spec";
|
|
11
|
-
"feature-spec": "feature-spec";
|
|
12
9
|
epic: "epic";
|
|
10
|
+
"feature-spec": "feature-spec";
|
|
11
|
+
idea: "idea";
|
|
12
|
+
"quick-fix": "quick-fix";
|
|
13
13
|
}>;
|
|
14
14
|
description: z.ZodString;
|
|
15
15
|
confidence: z.ZodNumber;
|
|
@@ -38,9 +38,9 @@ export declare const ReviewFeedbackV1Schema: z.ZodObject<{
|
|
|
38
38
|
}>;
|
|
39
39
|
reviewer: z.ZodObject<{
|
|
40
40
|
kind: z.ZodEnum<{
|
|
41
|
+
automation: "automation";
|
|
41
42
|
human: "human";
|
|
42
43
|
"spec-review-agent": "spec-review-agent";
|
|
43
|
-
automation: "automation";
|
|
44
44
|
}>;
|
|
45
45
|
agent: z.ZodString;
|
|
46
46
|
verdict: z.ZodEnum<{
|
|
@@ -73,8 +73,8 @@ export declare const ValidationReportV1Schema: z.ZodObject<{
|
|
|
73
73
|
}, z.core.$strip>>;
|
|
74
74
|
reviewer: z.ZodObject<{
|
|
75
75
|
kind: z.ZodEnum<{
|
|
76
|
-
human: "human";
|
|
77
76
|
automation: "automation";
|
|
77
|
+
human: "human";
|
|
78
78
|
"implementation-review-agent": "implementation-review-agent";
|
|
79
79
|
}>;
|
|
80
80
|
agent: z.ZodString;
|
|
@@ -104,13 +104,13 @@ export declare const ValidationReportV1Schema: z.ZodObject<{
|
|
|
104
104
|
tag: z.ZodString;
|
|
105
105
|
severity: z.ZodEnum<{
|
|
106
106
|
blocker: "blocker";
|
|
107
|
-
warning: "warning";
|
|
108
107
|
info: "info";
|
|
108
|
+
warning: "warning";
|
|
109
109
|
}>;
|
|
110
110
|
confidence: z.ZodEnum<{
|
|
111
|
+
high: "high";
|
|
111
112
|
low: "low";
|
|
112
113
|
medium: "medium";
|
|
113
|
-
high: "high";
|
|
114
114
|
}>;
|
|
115
115
|
target: z.ZodString;
|
|
116
116
|
line: z.ZodOptional<z.ZodNumber>;
|
|
@@ -119,10 +119,10 @@ export declare const ValidationReportV1Schema: z.ZodObject<{
|
|
|
119
119
|
blocksDone: z.ZodBoolean;
|
|
120
120
|
evidenceSource: z.ZodArray<z.ZodObject<{
|
|
121
121
|
source: z.ZodEnum<{
|
|
122
|
-
spec: "spec";
|
|
123
|
-
runtime: "runtime";
|
|
124
122
|
policy: "policy";
|
|
125
123
|
"project-config": "project-config";
|
|
124
|
+
runtime: "runtime";
|
|
125
|
+
spec: "spec";
|
|
126
126
|
}>;
|
|
127
127
|
key: z.ZodString;
|
|
128
128
|
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
@@ -142,10 +142,10 @@ export declare const ValidationReportV1Schema: z.ZodObject<{
|
|
|
142
142
|
}, z.core.$strip>>;
|
|
143
143
|
evidence: z.ZodArray<z.ZodObject<{
|
|
144
144
|
source: z.ZodEnum<{
|
|
145
|
-
spec: "spec";
|
|
146
|
-
runtime: "runtime";
|
|
147
145
|
policy: "policy";
|
|
148
146
|
"project-config": "project-config";
|
|
147
|
+
runtime: "runtime";
|
|
148
|
+
spec: "spec";
|
|
149
149
|
}>;
|
|
150
150
|
key: z.ZodString;
|
|
151
151
|
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
@@ -159,11 +159,11 @@ export declare const ARTIFACT_SCHEMAS: {
|
|
|
159
159
|
readonly intake: z.ZodObject<{
|
|
160
160
|
schema_version: z.ZodString;
|
|
161
161
|
kind: z.ZodEnum<{
|
|
162
|
-
idea: "idea";
|
|
163
|
-
"quick-fix": "quick-fix";
|
|
164
162
|
"bug-spec": "bug-spec";
|
|
165
|
-
"feature-spec": "feature-spec";
|
|
166
163
|
epic: "epic";
|
|
164
|
+
"feature-spec": "feature-spec";
|
|
165
|
+
idea: "idea";
|
|
166
|
+
"quick-fix": "quick-fix";
|
|
167
167
|
}>;
|
|
168
168
|
description: z.ZodString;
|
|
169
169
|
confidence: z.ZodNumber;
|
|
@@ -192,9 +192,9 @@ export declare const ARTIFACT_SCHEMAS: {
|
|
|
192
192
|
}>;
|
|
193
193
|
reviewer: z.ZodObject<{
|
|
194
194
|
kind: z.ZodEnum<{
|
|
195
|
+
automation: "automation";
|
|
195
196
|
human: "human";
|
|
196
197
|
"spec-review-agent": "spec-review-agent";
|
|
197
|
-
automation: "automation";
|
|
198
198
|
}>;
|
|
199
199
|
agent: z.ZodString;
|
|
200
200
|
verdict: z.ZodEnum<{
|
|
@@ -227,8 +227,8 @@ export declare const ARTIFACT_SCHEMAS: {
|
|
|
227
227
|
}, z.core.$strip>>;
|
|
228
228
|
reviewer: z.ZodObject<{
|
|
229
229
|
kind: z.ZodEnum<{
|
|
230
|
-
human: "human";
|
|
231
230
|
automation: "automation";
|
|
231
|
+
human: "human";
|
|
232
232
|
"implementation-review-agent": "implementation-review-agent";
|
|
233
233
|
}>;
|
|
234
234
|
agent: z.ZodString;
|
|
@@ -258,13 +258,13 @@ export declare const ARTIFACT_SCHEMAS: {
|
|
|
258
258
|
tag: z.ZodString;
|
|
259
259
|
severity: z.ZodEnum<{
|
|
260
260
|
blocker: "blocker";
|
|
261
|
-
warning: "warning";
|
|
262
261
|
info: "info";
|
|
262
|
+
warning: "warning";
|
|
263
263
|
}>;
|
|
264
264
|
confidence: z.ZodEnum<{
|
|
265
|
+
high: "high";
|
|
265
266
|
low: "low";
|
|
266
267
|
medium: "medium";
|
|
267
|
-
high: "high";
|
|
268
268
|
}>;
|
|
269
269
|
target: z.ZodString;
|
|
270
270
|
line: z.ZodOptional<z.ZodNumber>;
|
|
@@ -273,10 +273,10 @@ export declare const ARTIFACT_SCHEMAS: {
|
|
|
273
273
|
blocksDone: z.ZodBoolean;
|
|
274
274
|
evidenceSource: z.ZodArray<z.ZodObject<{
|
|
275
275
|
source: z.ZodEnum<{
|
|
276
|
-
spec: "spec";
|
|
277
|
-
runtime: "runtime";
|
|
278
276
|
policy: "policy";
|
|
279
277
|
"project-config": "project-config";
|
|
278
|
+
runtime: "runtime";
|
|
279
|
+
spec: "spec";
|
|
280
280
|
}>;
|
|
281
281
|
key: z.ZodString;
|
|
282
282
|
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
@@ -296,10 +296,10 @@ export declare const ARTIFACT_SCHEMAS: {
|
|
|
296
296
|
}, z.core.$strip>>;
|
|
297
297
|
evidence: z.ZodArray<z.ZodObject<{
|
|
298
298
|
source: z.ZodEnum<{
|
|
299
|
-
spec: "spec";
|
|
300
|
-
runtime: "runtime";
|
|
301
299
|
policy: "policy";
|
|
302
300
|
"project-config": "project-config";
|
|
301
|
+
runtime: "runtime";
|
|
302
|
+
spec: "spec";
|
|
303
303
|
}>;
|
|
304
304
|
key: z.ZodString;
|
|
305
305
|
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// engine/human-summary.ts — Plain-language one/two-sentence summaries for tool responses.
|
|
2
|
-
// Rules: no IDs/hashes, no jargon, max 2 sentences,
|
|
2
|
+
// Rules: no IDs/hashes, no jargon, max 2 sentences, and no invented next step for terminal states.
|
|
3
3
|
/** create_spec — plan was created successfully */
|
|
4
4
|
export function buildCreateSpecSummary(title, devHours) {
|
|
5
5
|
const hours = devHours > 0 ? `Estimated ${String(devHours)}h` : 'Estimation ready';
|
|
@@ -8,7 +8,7 @@ export function buildCreateSpecSummary(title, devHours) {
|
|
|
8
8
|
/** update_status — status transition completed */
|
|
9
9
|
export function buildUpdateStatusSummary(title, newStatus) {
|
|
10
10
|
if (newStatus === 'done') {
|
|
11
|
-
return `Done! "${title}" is complete and recorded
|
|
11
|
+
return `Done! "${title}" is complete and recorded.`;
|
|
12
12
|
}
|
|
13
13
|
if (newStatus === 'approved') {
|
|
14
14
|
return `"${title}" is approved and ready to build. Start implementing whenever you like.`;
|
|
@@ -20,7 +20,7 @@ export function buildUpdateStatusSummary(title, newStatus) {
|
|
|
20
20
|
return `"${title}" is now under review. Approve it or request changes to move forward.`;
|
|
21
21
|
}
|
|
22
22
|
if (newStatus === 'discarded') {
|
|
23
|
-
return `"${title}" has been discarded
|
|
23
|
+
return `"${title}" has been discarded.`;
|
|
24
24
|
}
|
|
25
25
|
return `"${title}" moved to ${newStatus}. Check the next step suggestion above to continue.`;
|
|
26
26
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const IMPLEMENTATION_CONTRACT_SECTION = "Implementation Contract";
|
|
2
|
-
export declare const IMPLEMENTATION_CONTRACT_SUBSECTIONS: readonly [
|
|
2
|
+
export declare const IMPLEMENTATION_CONTRACT_SUBSECTIONS: readonly ['User Outcome', 'Behavior Contract', 'File-Level Work Plan', 'Acceptance-To-Verification Map', 'Edge Cases And Failure Modes', 'Non-Goals And Forbidden Approaches', 'Verification Commands'];
|
|
3
3
|
export declare function hasImplementationContract(specBody: string): boolean;
|
|
4
4
|
export declare function extractTopLevelSection(body: string, sectionName: string): string;
|
|
5
5
|
//# sourceMappingURL=common.d.ts.map
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* "validate input") but rarely represent a domain concept worth searching for.
|
|
4
4
|
* Keep lowercase; callers normalize before lookup.
|
|
5
5
|
*/
|
|
6
|
-
export declare const ACTION_VERBS: readonly [
|
|
6
|
+
export declare const ACTION_VERBS: readonly ['commit', 'write', 'read', 'delete', 'create', 'update', 'export', 'import', 'store', 'send', 'push', 'pull', 'sync', 'check', 'validate', 'verify', 'fetch', 'load', 'save', 'remove', 'add', 'get', 'set', 'put', 'post', 'patch', 'run', 'execute', 'parse', 'render', 'build', 'compile'];
|
|
7
7
|
/**
|
|
8
8
|
* Generic English connectors and filler nouns that do not narrow a match.
|
|
9
9
|
* Kept deliberately small — domain stopwords should go through
|
|
10
10
|
* `KeywordMatchOptions.customStopwords`.
|
|
11
11
|
*/
|
|
12
|
-
export declare const COMMON_STOPWORDS: readonly [
|
|
12
|
+
export declare const COMMON_STOPWORDS: readonly ['the', 'and', 'for', 'that', 'this', 'with', 'from', 'into', 'onto', 'version', 'control', 'system', 'data', 'value', 'field', 'record', 'user', 'item', 'thing', 'stuff', 'something', 'anything'];
|
|
13
13
|
/**
|
|
14
14
|
* True if `word` is a known action verb (case-insensitive).
|
|
15
15
|
*/
|
|
@@ -5,7 +5,7 @@ import type { ProviderRenderInput, ProviderRenderOutput, SpecRenderer } from '..
|
|
|
5
5
|
*/
|
|
6
6
|
export declare const CLAUDE_TOOL_CALLOUT_MAP: Readonly<Record<string, readonly string[]>>;
|
|
7
7
|
export declare class ClaudeRenderer implements SpecRenderer {
|
|
8
|
-
readonly kind:
|
|
8
|
+
readonly kind: 'claude';
|
|
9
9
|
render(input: ProviderRenderInput): Promise<ProviderRenderOutput>;
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -5,7 +5,7 @@ import type { ProviderRenderInput, ProviderRenderOutput, SpecRenderer } from '..
|
|
|
5
5
|
*/
|
|
6
6
|
export declare const GEMINI_API_VERSION = "v1beta";
|
|
7
7
|
export declare class GeminiRenderer implements SpecRenderer {
|
|
8
|
-
readonly kind:
|
|
8
|
+
readonly kind: 'gemini';
|
|
9
9
|
render(input: ProviderRenderInput): Promise<ProviderRenderOutput>;
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ProviderRenderInput, ProviderRenderOutput, SpecRenderer } from '../../../types/provider-adapter.js';
|
|
2
2
|
export declare class GPT4Renderer implements SpecRenderer {
|
|
3
|
-
readonly kind:
|
|
3
|
+
readonly kind: 'gpt4';
|
|
4
4
|
render(input: ProviderRenderInput): Promise<ProviderRenderOutput>;
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=gpt4.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ProviderRenderInput, ProviderRenderOutput, SpecRenderer } from '../../../types/provider-adapter.js';
|
|
2
2
|
export declare class GenericMarkdownRenderer implements SpecRenderer {
|
|
3
|
-
readonly kind:
|
|
3
|
+
readonly kind: 'markdown';
|
|
4
4
|
render(input: ProviderRenderInput): Promise<ProviderRenderOutput>;
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -6,7 +6,7 @@ import type { AtomicWriteOptions } from '../../types/safety.js';
|
|
|
6
6
|
export declare class SpecFrozenError extends Error {
|
|
7
7
|
readonly specId: string;
|
|
8
8
|
readonly fixHint: string;
|
|
9
|
-
readonly code:
|
|
9
|
+
readonly code: 'SPEC_FROZEN';
|
|
10
10
|
constructor(specId: string, fixHint: string);
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
@@ -3,7 +3,7 @@ declare const HEARTBEAT_INTERVAL_MS = 30000;
|
|
|
3
3
|
export { HEARTBEAT_INTERVAL_MS };
|
|
4
4
|
/** Thrown by withSpecLock when the lock is held by another live session. */
|
|
5
5
|
export declare class LockBusyError extends Error {
|
|
6
|
-
readonly code:
|
|
6
|
+
readonly code: 'LOCK_BUSY';
|
|
7
7
|
readonly holder: CrossProcessLockMetadata;
|
|
8
8
|
constructor(holder: CrossProcessLockMetadata);
|
|
9
9
|
}
|
|
@@ -1,28 +1,58 @@
|
|
|
1
1
|
// engine/security-analyzer/profile-detector.ts — Feature profile and auth detection
|
|
2
2
|
// SPEC-029
|
|
3
|
+
import { hasAnyAffirmedMatch } from '../text-signal-boundaries.js';
|
|
3
4
|
// === Feature profile detection ===
|
|
4
|
-
const
|
|
5
|
-
{ keywords: ['api', 'endpoint', 'route', 'rest', 'graphql'], profile: 'public-api' },
|
|
5
|
+
const PROFILE_SIGNALS = [
|
|
6
6
|
{
|
|
7
|
-
|
|
7
|
+
patterns: [
|
|
8
|
+
/(?<!compiler\s)\bapi\b(?!\s+(?:compatibility|surface|package|type))/i,
|
|
9
|
+
/\b(?:public|external|rest|http)[ -]?api\b/i,
|
|
10
|
+
/\bapi\s+(?:endpoint|route|request|response|server)\b/i,
|
|
11
|
+
/\b(?:http|rest)\s+(?:endpoint|route)\b/i,
|
|
12
|
+
/\bgraphql\s+(?:api|endpoint|resolver|schema)\b/i,
|
|
13
|
+
],
|
|
14
|
+
profile: 'public-api',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
patterns: [
|
|
18
|
+
/\b(?:auth(?:entication)?|login|sign[ -]?up|register\s+user|password|jwt|oauth2?|session\s+auth)\b/i,
|
|
19
|
+
],
|
|
8
20
|
profile: 'auth',
|
|
9
21
|
},
|
|
10
22
|
{
|
|
11
|
-
|
|
23
|
+
patterns: [
|
|
24
|
+
/\b(?:database|db-access|postgres(?:ql)?|mysql|sqlite|mongodb|dynamodb)\b/i,
|
|
25
|
+
/\b(?:sql|nosql)\s+(?:query|statement|table|database)\b/i,
|
|
26
|
+
/\b(?:database|persistence)\s+(?:query|schema|table|migration|transaction)\b/i,
|
|
27
|
+
/\b(?:orm|prisma|drizzle|typeorm|sequelize)\b/i,
|
|
28
|
+
],
|
|
12
29
|
profile: 'db-access',
|
|
13
30
|
},
|
|
14
|
-
{ keywords: ['upload', 'file', 'attachment', 'media', 'image'], profile: 'file-upload' },
|
|
15
|
-
{ keywords: ['html', 'render', 'template', 'ui', 'frontend', 'view'], profile: 'html-render' },
|
|
16
31
|
{
|
|
17
|
-
|
|
32
|
+
patterns: [
|
|
33
|
+
/\bfile[ -]?upload\b/i,
|
|
34
|
+
/\b(?:upload|attach)(?:ed|ment|ing|s)?\s+(?:file|image|media)\b/i,
|
|
35
|
+
],
|
|
36
|
+
profile: 'file-upload',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
patterns: [
|
|
40
|
+
/\b(?:html\s+render|render(?:ed|ing)?\s+html|ui\s+(?:component|view|rendering)|frontend\s+view|template\s+render)\b/i,
|
|
41
|
+
],
|
|
42
|
+
profile: 'html-render',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
patterns: [
|
|
46
|
+
/\b(?:pii|personal\s+(?:data|information)|user\s+email|email\s+address|phone\s+number|ssn|gdpr|privacy)\b/i,
|
|
47
|
+
],
|
|
18
48
|
profile: 'pii-data',
|
|
19
49
|
},
|
|
20
50
|
];
|
|
21
51
|
export function detectSecurityProfiles(title, description, tags) {
|
|
22
|
-
const
|
|
52
|
+
const text = `${title}\n${description}\n${tags.join(' ')}`;
|
|
23
53
|
const profiles = [];
|
|
24
|
-
for (const {
|
|
25
|
-
if (
|
|
54
|
+
for (const { patterns, profile } of PROFILE_SIGNALS) {
|
|
55
|
+
if (hasAnyAffirmedMatch(text, patterns)) {
|
|
26
56
|
profiles.push(profile);
|
|
27
57
|
}
|
|
28
58
|
}
|
|
@@ -33,20 +63,22 @@ export function detectSecurityProfiles(title, description, tags) {
|
|
|
33
63
|
}
|
|
34
64
|
// === Auth mechanism detection ===
|
|
35
65
|
export function detectAuthMechanism(description, tags) {
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
66
|
+
const text = `${description}\n${tags.join(' ')}`;
|
|
67
|
+
if (hasAnyAffirmedMatch(text, [/\b(?:jwt|json\s+web\s+token)\b/i])) {
|
|
38
68
|
return 'jwt';
|
|
39
69
|
}
|
|
40
|
-
if (
|
|
70
|
+
if (hasAnyAffirmedMatch(text, [/\boauth2?\b/i])) {
|
|
41
71
|
return 'oauth2';
|
|
42
72
|
}
|
|
43
|
-
if (
|
|
73
|
+
if (hasAnyAffirmedMatch(text, [/\bapi[ _-]?key\b/i])) {
|
|
44
74
|
return 'api-key';
|
|
45
75
|
}
|
|
46
|
-
if (
|
|
76
|
+
if (hasAnyAffirmedMatch(text, [
|
|
77
|
+
/\b(?:session[ -]?based\s+authentication|session\s+auth|session\s+cookie)\b/i,
|
|
78
|
+
])) {
|
|
47
79
|
return 'session';
|
|
48
80
|
}
|
|
49
|
-
if (
|
|
81
|
+
if (hasAnyAffirmedMatch(text, [/\bbasic\s+auth(?:entication)?\b/i])) {
|
|
50
82
|
return 'basic';
|
|
51
83
|
}
|
|
52
84
|
return 'none';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Spanish copulative verbs that indicate non-testable, state-describing criteria. */
|
|
2
|
-
export declare const COPULA_VERBS_ES: readonly [
|
|
2
|
+
export declare const COPULA_VERBS_ES: readonly ['es', 'son', 'está', 'están', 'tiene', 'tienen'];
|
|
3
3
|
/** English copulative verbs that indicate non-testable, state-describing criteria. */
|
|
4
|
-
export declare const COPULA_VERBS_EN: readonly [
|
|
4
|
+
export declare const COPULA_VERBS_EN: readonly ['is', 'are', 'has', 'have', 'exists', 'contains'];
|
|
5
5
|
export interface CopulaCheckResult {
|
|
6
6
|
hit: boolean;
|
|
7
7
|
verb?: string;
|