@planu/cli 4.11.1 → 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.
Files changed (116) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/engine/cascade-hooks/registry.js +0 -2
  3. package/dist/engine/challenge-scenarios-loader.d.ts +3 -3
  4. package/dist/engine/compliance-injector.d.ts +3 -3
  5. package/dist/engine/dashboard/kanban-logic.d.ts +1 -1
  6. package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +1 -1
  7. package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +1 -1
  8. package/dist/engine/elicitation/answer-extractor.d.ts +1 -4
  9. package/dist/engine/elicitation/answer-extractor.js +197 -68
  10. package/dist/engine/elicitation/decision-gap-detector.js +59 -79
  11. package/dist/engine/elicitation/index.d.ts +0 -1
  12. package/dist/engine/elicitation/index.js +0 -1
  13. package/dist/engine/elicitation/question-grounding-gate.js +83 -34
  14. package/dist/engine/evidence-gates/lifecycle-gate.js +27 -14
  15. package/dist/engine/evidence-index/index-builder.js +7 -2
  16. package/dist/engine/frontmatter-sha/frozen-fields.d.ts +1 -1
  17. package/dist/engine/handoff-artifacts/schemas/red-tests-handoff.d.ts +2 -2
  18. package/dist/engine/handoff-artifacts/schemas.d.ts +22 -22
  19. package/dist/engine/human-summary.js +3 -3
  20. package/dist/engine/implementation-contract/common.d.ts +1 -1
  21. package/dist/engine/keyword-matcher/stopwords.d.ts +2 -2
  22. package/dist/engine/provider-adapters/adapters/claude.d.ts +1 -1
  23. package/dist/engine/provider-adapters/adapters/gemini.d.ts +1 -1
  24. package/dist/engine/provider-adapters/adapters/gpt4.d.ts +1 -1
  25. package/dist/engine/provider-adapters/adapters/markdown.d.ts +1 -1
  26. package/dist/engine/safety/atomic-write-file.d.ts +1 -1
  27. package/dist/engine/safety/cross-process-lock.d.ts +1 -1
  28. package/dist/engine/safety/path-sanitizer.d.ts +1 -1
  29. package/dist/engine/security-analyzer/profile-detector.js +48 -16
  30. package/dist/engine/spec-format/copula-blacklist.d.ts +2 -2
  31. package/dist/engine/spec-format/metadata-value-policy.d.ts +20 -20
  32. package/dist/engine/text-signal-boundaries.js +45 -12
  33. package/dist/engine/universal-rules/rules/planu-modes.js +0 -2
  34. package/dist/engine/well-known/manifest-schema.d.ts +33 -33
  35. package/dist/engine/workers/schema.d.ts +3 -3
  36. package/dist/hosts/claude-code/ux/mcp-resources.d.ts +4 -4
  37. package/dist/hosts/codex/rest-shim.d.ts +1 -1
  38. package/dist/native/lightweight-command-catalog.d.ts +30 -30
  39. package/dist/server/openapi-spec.d.ts +132 -132
  40. package/dist/tools/challenge-spec/event-challenge-scenarios.js +8 -27
  41. package/dist/tools/challenge-spec/resilience-challenge-scenarios-a.js +16 -32
  42. package/dist/tools/challenge-spec/resilience-challenge-scenarios-b.js +15 -20
  43. package/dist/tools/challenge-spec/scenario-collector.d.ts +13 -0
  44. package/dist/tools/challenge-spec/scenario-collector.js +86 -0
  45. package/dist/tools/challenge-spec/scenarios-data.js +10 -9
  46. package/dist/tools/challenge-spec/scenarios-failure.js +38 -54
  47. package/dist/tools/challenge-spec/scenarios-scale.js +14 -15
  48. package/dist/tools/challenge-spec/scenarios-security.d.ts +1 -1
  49. package/dist/tools/challenge-spec/scenarios-security.js +18 -11
  50. package/dist/tools/challenge-spec/scenarios-utils.d.ts +31 -5
  51. package/dist/tools/challenge-spec/scenarios-utils.js +102 -6
  52. package/dist/tools/challenge-spec/security-challenge-scenarios.d.ts +1 -1
  53. package/dist/tools/challenge-spec/security-challenge-scenarios.js +20 -25
  54. package/dist/tools/challenge-spec.js +39 -118
  55. package/dist/tools/compliance-gate-handler.d.ts +1 -1
  56. package/dist/tools/create-spec.js +4 -24
  57. package/dist/tools/delete-first/suggest-deletions.d.ts +1 -1
  58. package/dist/tools/generate-batch-script.d.ts +2 -2
  59. package/dist/tools/init-project/rules-generator.js +0 -1
  60. package/dist/tools/oauth-handler.d.ts +1 -1
  61. package/dist/tools/register-sdd-tools.d.ts +1 -1
  62. package/dist/tools/render-spec-for-provider.d.ts +2 -2
  63. package/dist/tools/scan-orphan-spec-refs.d.ts +6 -6
  64. package/dist/tools/schemas/agents.d.ts +7 -7
  65. package/dist/tools/schemas/analysis.d.ts +11 -11
  66. package/dist/tools/schemas/delete.d.ts +5 -5
  67. package/dist/tools/schemas/github.d.ts +4 -4
  68. package/dist/tools/schemas/ide-config.d.ts +12 -12
  69. package/dist/tools/schemas/infra.d.ts +9 -9
  70. package/dist/tools/schemas/lifecycle.d.ts +50 -50
  71. package/dist/tools/schemas/llm-provider-schemas.d.ts +7 -7
  72. package/dist/tools/schemas/plugins-schemas.d.ts +12 -12
  73. package/dist/tools/schemas/project-graph.d.ts +6 -6
  74. package/dist/tools/schemas/rbac.d.ts +2 -2
  75. package/dist/tools/schemas/registry.d.ts +3 -3
  76. package/dist/tools/schemas/runtime-security.d.ts +2 -2
  77. package/dist/tools/schemas/session.d.ts +2 -2
  78. package/dist/tools/schemas/spec.d.ts +15 -15
  79. package/dist/tools/schemas/token-intelligence.d.ts +6 -6
  80. package/dist/tools/schemas/token-optimization.d.ts +2 -2
  81. package/dist/tools/schemas/workers-schema.d.ts +4 -4
  82. package/dist/tools/skill-bootstrap-handler.d.ts +7 -7
  83. package/dist/tools/tool-registry/deprecated-stubs.d.ts +1 -1
  84. package/dist/tools/triage-request.d.ts +3 -3
  85. package/dist/tools/update-status/file-sync.d.ts +1 -1
  86. package/dist/tools/update-status/file-sync.js +10 -45
  87. package/dist/tools/update-status/index.js +28 -27
  88. package/dist/tools/update-status/response-builder.d.ts +1 -1
  89. package/dist/tools/update-status/response-builder.js +11 -19
  90. package/dist/tools/update-status/transition-guard.js +1 -1
  91. package/dist/tools/update-status-actions.d.ts +0 -1
  92. package/dist/tools/update-status-actions.js +10 -26
  93. package/dist/tools/update-status-convention-gate.d.ts +1 -6
  94. package/dist/tools/update-status-convention-gate.js +9 -33
  95. package/dist/types/docs.d.ts +0 -4
  96. package/dist/types/env.d.ts +0 -4
  97. package/dist/types/estimation.d.ts +0 -2
  98. package/dist/types/multi-teammate-review.d.ts +4 -4
  99. package/dist/types/plugin-install.d.ts +7 -7
  100. package/package.json +21 -18
  101. package/planu-native.json +1 -1
  102. package/planu-plugin.json +1 -1
  103. package/src/i18n/messages/en.json +0 -72
  104. package/src/i18n/messages/es.json +0 -72
  105. package/src/i18n/messages/pt.json +0 -72
  106. package/dist/config/elicitation-dimensions.json +0 -51
  107. package/dist/engine/actuals-estimator.d.ts +0 -14
  108. package/dist/engine/actuals-estimator.js +0 -64
  109. package/dist/engine/cascade-hooks/hooks/living-specs.hook.d.ts +0 -3
  110. package/dist/engine/cascade-hooks/hooks/living-specs.hook.js +0 -19
  111. package/dist/engine/elicitation/option-builder.d.ts +0 -9
  112. package/dist/engine/elicitation/option-builder.js +0 -85
  113. package/dist/engine/progress-writer.d.ts +0 -10
  114. package/dist/engine/progress-writer.js +0 -63
  115. package/dist/tools/update-status/mode-hints.d.ts +0 -4
  116. 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 lowerEvidence = [context.requestText, ...context.gap.evidence].join(' ').toLowerCase();
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|\bthis changes\b|\baffects?\b|\bbecause\b|\bbecomes?\b/i.test(text)) {
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
- return { passed: true };
73
+ const optionFailure = validateOptions(question, context);
74
+ return optionFailure ?? { passed: true };
31
75
  }
32
- function tokenSet(text) {
33
- return new Set(text
34
- .replace(/[^a-z0-9 ]/gi, ' ')
35
- .split(/\s+/)
36
- .filter((token) => !STOP_WORDS.has(token) && token.length > 2));
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: /\b(api|rest|endpoint|openapi|http route|controller)\b/i,
3
- graphql: /\b(graphql|gql|schema\.graphql|resolver)\b/i,
4
- event: /\b(event|webhook|queue|topic|message broker|kafka|rabbitmq|pubsub)\b/i,
5
- ui: /\b(ui contract|component contract|screen contract|design contract)\b/i,
6
- mcp: /\b(mcp|tool contract|model context protocol|registerTool|server tool)\b/i,
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
- return value
13
- .replace(/^-\s*\[[ x]\]\s*/i, '')
14
- .replace(/^(AC\d+)\s*[:.)-]?\s*/i, '')
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, pattern] of Object.entries(CONTRACT_HINTS)) {
37
- if (pattern.test(text)) {
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
- return value
159
- .replace(/^-\s*\[[ x]\]\s*/i, '')
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 ["status", "scenarios", "spec_version", "id", "type", "target"];
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
- vitest: "vitest";
11
+ playwright: "playwright";
12
12
  pytest: "pytest";
13
13
  rspec: "rspec";
14
- playwright: "playwright";
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, always end with a next-step suggestion.
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. Run estimate or start a new plan when you're ready.`;
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. Create a new plan whenever you want to restart.`;
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 ["User Outcome", "Behavior Contract", "File-Level Work Plan", "Acceptance-To-Verification Map", "Edge Cases And Failure Modes", "Non-Goals And Forbidden Approaches", "Verification Commands"];
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 ["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"];
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 ["the", "and", "for", "that", "this", "with", "from", "into", "onto", "version", "control", "system", "data", "value", "field", "record", "user", "item", "thing", "stuff", "something", "anything"];
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: "claude";
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: "gemini";
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: "gpt4";
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: "markdown";
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: "SPEC_FROZEN";
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: "LOCK_BUSY";
6
+ readonly code: 'LOCK_BUSY';
7
7
  readonly holder: CrossProcessLockMetadata;
8
8
  constructor(holder: CrossProcessLockMetadata);
9
9
  }
@@ -1,6 +1,6 @@
1
1
  import type { SafePath } from '../../types/safety.js';
2
2
  export declare class PathViolationError extends Error {
3
- readonly code: "PATH_VIOLATION";
3
+ readonly code: 'PATH_VIOLATION';
4
4
  readonly input: string;
5
5
  constructor(message: string, input: string);
6
6
  }
@@ -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 PROFILE_KEYWORDS = [
5
- { keywords: ['api', 'endpoint', 'route', 'rest', 'graphql'], profile: 'public-api' },
5
+ const PROFILE_SIGNALS = [
6
6
  {
7
- keywords: ['auth', 'login', 'signup', 'register', 'password', 'jwt', 'oauth', 'session'],
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
- keywords: ['database', 'query', 'sql', 'db', 'schema', 'orm', 'repository'],
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
- keywords: ['pii', 'personal', 'email', 'phone', 'ssn', 'gdpr', 'privacy'],
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 lower = `${title} ${description} ${tags.join(' ')}`.toLowerCase();
52
+ const text = `${title}\n${description}\n${tags.join(' ')}`;
23
53
  const profiles = [];
24
- for (const { keywords, profile } of PROFILE_KEYWORDS) {
25
- if (keywords.some((kw) => lower.includes(kw))) {
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 lower = `${description} ${tags.join(' ')}`.toLowerCase();
37
- if (lower.includes('jwt') || lower.includes('json web token')) {
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 (lower.includes('oauth') || lower.includes('oauth2')) {
70
+ if (hasAnyAffirmedMatch(text, [/\boauth2?\b/i])) {
41
71
  return 'oauth2';
42
72
  }
43
- if (lower.includes('api key') || lower.includes('apikey') || lower.includes('api_key')) {
73
+ if (hasAnyAffirmedMatch(text, [/\bapi[ _-]?key\b/i])) {
44
74
  return 'api-key';
45
75
  }
46
- if (lower.includes('session') || lower.includes('cookie')) {
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 (lower.includes('basic auth') || lower.includes('basic authentication')) {
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 ["es", "son", "está", "están", "tiene", "tienen"];
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 ["is", "are", "has", "have", "exists", "contains"];
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;