@skillstech/thunderlang 0.1.6

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 (72) hide show
  1. package/CHANGELOG.md +375 -0
  2. package/LICENSE +21 -0
  3. package/README.md +165 -0
  4. package/dist/core.cjs +6768 -0
  5. package/dist/index.cjs +9308 -0
  6. package/intent-graph.schema.json +687 -0
  7. package/package.json +84 -0
  8. package/src/ai-core.mjs +67 -0
  9. package/src/ai-events.mjs +56 -0
  10. package/src/ai.mjs +324 -0
  11. package/src/arch.mjs +55 -0
  12. package/src/atlas.mjs +118 -0
  13. package/src/changes.mjs +74 -0
  14. package/src/classification.mjs +36 -0
  15. package/src/cli.mjs +1534 -0
  16. package/src/codegen.mjs +214 -0
  17. package/src/compile.mjs +142 -0
  18. package/src/comprehension.mjs +130 -0
  19. package/src/conflict.mjs +0 -0
  20. package/src/core.d.ts +99 -0
  21. package/src/core.mjs +92 -0
  22. package/src/data-schema.mjs +137 -0
  23. package/src/decision.mjs +38 -0
  24. package/src/distributed.mjs +48 -0
  25. package/src/draft.mjs +101 -0
  26. package/src/drift.mjs +177 -0
  27. package/src/emit.mjs +519 -0
  28. package/src/exporters.mjs +245 -0
  29. package/src/expr.mjs +245 -0
  30. package/src/fable.mjs +110 -0
  31. package/src/focus.mjs +151 -0
  32. package/src/format.mjs +55 -0
  33. package/src/governance.mjs +100 -0
  34. package/src/graph-source.mjs +292 -0
  35. package/src/guard.mjs +105 -0
  36. package/src/guardian.mjs +98 -0
  37. package/src/hash.mjs +89 -0
  38. package/src/importers.mjs +194 -0
  39. package/src/index.d.ts +725 -0
  40. package/src/index.mjs +185 -0
  41. package/src/intellisense.mjs +210 -0
  42. package/src/intent-atlas.mjs +77 -0
  43. package/src/intent-graph.mjs +346 -0
  44. package/src/intent-ir.mjs +144 -0
  45. package/src/intent-schema.mjs +215 -0
  46. package/src/ledger.mjs +109 -0
  47. package/src/lifecycle.mjs +56 -0
  48. package/src/lift.mjs +693 -0
  49. package/src/lsp.mjs +152 -0
  50. package/src/mcp.mjs +158 -0
  51. package/src/migrate.mjs +118 -0
  52. package/src/outcome.mjs +93 -0
  53. package/src/parse.mjs +733 -0
  54. package/src/patch.mjs +364 -0
  55. package/src/privacy.mjs +61 -0
  56. package/src/proof-schema.mjs +129 -0
  57. package/src/report.mjs +84 -0
  58. package/src/runtime.mjs +96 -0
  59. package/src/sarif.mjs +88 -0
  60. package/src/scan-queries.mjs +97 -0
  61. package/src/scan.mjs +87 -0
  62. package/src/security.mjs +73 -0
  63. package/src/select.mjs +80 -0
  64. package/src/semantic-diff.mjs +125 -0
  65. package/src/simulate.mjs +106 -0
  66. package/src/style.mjs +250 -0
  67. package/src/sync.mjs +103 -0
  68. package/src/testing.mjs +59 -0
  69. package/src/twelve-factor.mjs +173 -0
  70. package/src/verify-diff.mjs +105 -0
  71. package/src/xml.mjs +87 -0
  72. package/syntaxes/intent.tmLanguage.json +55 -0
package/src/index.mjs ADDED
@@ -0,0 +1,185 @@
1
+ // Public library entry for @skillstech/thunderlang.
2
+ //
3
+ // This is the importable API for consumers (e.g. SkillsTech Studio, Repo Mastery):
4
+ // import { parseIntent, compileSource, buildMissionIndex } from '@skillstech/thunderlang';
5
+ // The `intent` CLI (src/cli.mjs) is the other entry point; both use these same functions,
6
+ // so there is no duplicated compiler logic. Curated on purpose: only the stable public
7
+ // surface is re-exported here.
8
+
9
+ // Parsing
10
+ export { parseIntent, slug, KNOWN_LENSES } from './parse.mjs';
11
+
12
+ // Semantic analysis, graphs, and proof
13
+ export {
14
+ semanticDiagnostics,
15
+ buildContractGraph,
16
+ buildArchitectureGraph,
17
+ buildImplementationPlan,
18
+ buildProof,
19
+ sha256,
20
+ sha256hex,
21
+ COMPILER_VERSION,
22
+ PROOF_SCHEMA_VERSION,
23
+ SOURCE_PRODUCT,
24
+ } from './emit.mjs';
25
+
26
+ // Canonical proof envelope schema (intent-proof-v1) , the shared, signable proof shape
27
+ export {
28
+ PROOF_SCHEMA, CLAIM_STATUSES, PROOF_STATUSES, intentProofJsonSchema, validateProof,
29
+ } from './proof-schema.mjs';
30
+
31
+ // Compile + render (docs, Mermaid, test plan)
32
+ export { compileSource, renderMarkdown, renderLensDoc, renderMermaid, renderTestplan } from './compile.mjs';
33
+
34
+ // IntelliSense (completions / hover)
35
+ export { getCompletions, getHover, getCodeActions, autocorrectSource, SEMANTIC_TYPES } from './intellisense.mjs';
36
+
37
+ // IntentLift (code -> inferred intent)
38
+ export {
39
+ liftSource,
40
+ liftAll,
41
+ liftRepo,
42
+ languageForFile,
43
+ inferIntent,
44
+ renderLiftedIntent,
45
+ SUPPORTED_LANGUAGES,
46
+ SEED_SCHEMA,
47
+ normalizeSeeds,
48
+ } from './lift.mjs';
49
+
50
+ // Approve + drift round-trip (il-to-ot-drift-v1 handoff)
51
+ export { approveIntent, checkDrift, buildDriftHandoff, intentHash } from './drift.mjs';
52
+
53
+ // Mission Atlas index (mission-index-v1)
54
+ export { buildMissionIndex } from './atlas.mjs';
55
+
56
+ // Architecture rules (structured; OpenThunder's Architecture Lens checks against them)
57
+ export { parseArchitectureRules, violatesArchitecture } from './arch.mjs';
58
+
59
+ // Deterministic candidate selection (AI generates; IL + OT select on measurable rules)
60
+ export { parseSelection, regionMetrics, selectCandidate } from './select.mjs';
61
+
62
+ // Canonical Intent Graph + classification (intent-graph-v1) , the shared cross-product model
63
+ export { buildIntentGraph, INTENT_GRAPH_SCHEMA } from './intent-graph.mjs';
64
+ // Intent Atlas , the navigable/searchable whole-system map over the graph (directive #4)
65
+ export { buildAtlas, atlasNode, expandNode, searchAtlas, ATLAS_SCHEMA } from './intent-atlas.mjs';
66
+ export { diffGraphs, mergeGraphs } from './semantic-diff.mjs';
67
+ export { CLASSIFICATIONS, CONFIDENCE, UNSETTLED, classify, isFactual, BLOCKABLE_PHASES } from './classification.mjs';
68
+ export { composeConstraints, detectConflicts } from './conflict.mjs';
69
+ export { buildLifecycle, analyzeLifecycle } from './lifecycle.mjs';
70
+ export { analyzeDistributed } from './distributed.mjs';
71
+ export { analyzeDecision } from './decision.mjs';
72
+ // Governance: waivers , governed exceptions to blocking diagnostics (Gap 5)
73
+ export { applyWaivers, governanceDiagnostics, GOVERNANCE_SCHEMA } from './governance.mjs';
74
+ // Data purpose + privacy , purpose limitation on declared data elements (Gap 6)
75
+ export { analyzePrivacy, PRIVACY_SCHEMA, DATA_CLASSIFICATIONS, LAWFUL_BASES } from './privacy.mjs';
76
+ // Export adapters , decisions/lifecycles/temporal -> DMN / BPMN / NuSMV (interop)
77
+ export { toDMN, toBPMN, toSMV, toMermaid, toPlaywright, exportIntent, EXPORT_FORMATS } from './exporters.mjs';
78
+ // Data-shape export , typed fields -> JSON Schema / OpenAPI
79
+ export { toJSONSchema, toOpenAPI, typeToJsonSchema, isRecognizedType } from './data-schema.mjs';
80
+ // Import adapters , external DMN / BPMN -> ThunderLang source (round-trip)
81
+ export { fromDMN, fromBPMN, importIntent, importReport, detectFormat, IMPORT_FORMATS, IMPORT_SCHEMA } from './importers.mjs';
82
+ // Graph -> source , regenerate .intent from an Intent Graph (native round-trip)
83
+ export { graphToSource, GRAPH_SOURCE_SCHEMA } from './graph-source.mjs';
84
+ // Schema migrations , upgrade persisted graphs across schema versions
85
+ export {
86
+ migrateGraph, validateGraph, graphVersion, MIGRATIONS, SCHEMA_CHAIN, MIGRATION_SCHEMA,
87
+ renameNodeType, renameRelationshipType, backfillNodeField, dropNodeField,
88
+ } from './migrate.mjs';
89
+ export { parseXml, findAll, find, localName } from './xml.mjs';
90
+ // Intent Runtime , EXECUTABLE intent: evaluate decisions, simulate lifecycles (no AI)
91
+ export { evaluateDecision, simulateLifecycle, checkDecisionCases, RUNTIME_SCHEMA } from './runtime.mjs';
92
+ export { compileExpr, evalExpr, tokenize, ExprError } from './expr.mjs';
93
+ // First-class tests , self-verifying .intent files
94
+ export { runTests, TEST_SCHEMA } from './testing.mjs';
95
+ // Outcome contracts , executable commitments (evaluate an outcome against its result)
96
+ export { evaluateOutcomeContract, evaluateOutcomes, outcomeDiagnostics, parseValue, OUTCOME_SCHEMA } from './outcome.mjs';
97
+ // Security + type semantic pass , secrets on the bus, unauthenticated sensitive output, typos
98
+ export { securityDiagnostics, SECURITY_SCHEMA } from './security.mjs';
99
+ // SARIF 2.1.0 output , ThunderLang diagnostics in GitHub/GitLab code scanning + IDEs
100
+ export { toSarif, sarifLevel, SARIF_SCHEMA } from './sarif.mjs';
101
+ // Human <-> Structured <-> ThunderLang sync , Studio edits structured, IL stays source of truth
102
+ export { parseToStructured, proposeIntent, SYNC_SCHEMA } from './sync.mjs';
103
+ // Structural source editing , apply field edits in place, preserving comments + formatting
104
+ export { applyEdits, PATCH_SCHEMA } from './patch.mjs';
105
+ // Repo-wide intent health report (aggregate diagnostics + coverage across many files)
106
+ export { buildReport, REPORT_SCHEMA } from './report.mjs';
107
+ // Fable , the versioned rule authority + rich finding model (over the diagnostic catalog)
108
+ export { FABLE_SCHEMA, RISK_CATEGORIES, fableRuleFor, universalPack, toFinding } from './fable.mjs';
109
+ // Intent Scanner , intent -> Intent IR -> Fable findings -> risk themes (deterministic pipeline)
110
+ export { scanIntent, scanProject, SCAN_SCHEMA } from './scan.mjs';
111
+ // Intent Lens , Intent Scope + Focus Graph + Intent Brief (a focused subgraph of the Atlas)
112
+ export { FOCUS_SCHEMA, SCOPE_TYPES, FOCUS_REASONS, makeScope, buildFocusGraph, intentBrief } from './focus.mjs';
113
+ // Comprehension Contract , the C0..C7 understanding level (intent-comprehension-v1)
114
+ export { COMPREHENSION_SCHEMA, LEVELS as COMPREHENSION_LEVELS, comprehensionLevel, comprehensionReport } from './comprehension.mjs';
115
+ // Code generation , deterministic scaffolds from intent (intent-codegen-v1)
116
+ export { CODEGEN_SCHEMA, GENERATORS, toTypeScript, toCSharp, toJava } from './codegen.mjs';
117
+ // Change Lens , what a branch/PR changed by meaning (intent-changes-v1)
118
+ export { CHANGES_SCHEMA, changeReport } from './changes.mjs';
119
+ export { exprToJs, exprToCSharp, exprToJava, exprToCode } from './expr.mjs';
120
+ export { subjectName, intentRefId, skillRefId } from './parse.mjs';
121
+ // 12-Factor Agents conformance lens (twelve-factor-v1)
122
+ export { TWELVE_FACTOR_SCHEMA, twelveFactorReport, twelveFactorSummary } from './twelve-factor.mjs';
123
+ // Focused scanner query views (Part 3): risks / gaps / unverified / coverage / unknowns / contradictions
124
+ export {
125
+ VIEW_SCHEMA, VIEWS, risksView, gapsView, unverifiedView, coverageView, unknownsView, contradictionsView,
126
+ } from './scan-queries.mjs';
127
+ // Intent Guardian , continuous drift detection (what changed, what risk, what to reverify, stale learning)
128
+ export { guardianReport, GUARDIAN_SCHEMA } from './guardian.mjs';
129
+ // Intent Simulator , estimate a change's impact BEFORE implementation (blast radius + risk)
130
+ export { simulateChange, SIMULATE_SCHEMA } from './simulate.mjs';
131
+ // Intent Ledger , tamper-evident, append-only record of provenance, decisions, approvals, history
132
+ export {
133
+ LEDGER_SCHEMA, ENTRY_TYPES, emptyLedger, record, recordAll, verifyLedger,
134
+ recordIntentVersion, recordAssumption, recordApproval, recordRejection,
135
+ recordCorrection, recordRiskAcceptance, recordVerification, recordFinding, recordLessonVersion,
136
+ history, whyBuilt, approvalsFor, acceptedRisks, correctionsFor, staleLessons, explain,
137
+ recordDecision as recordLedgerDecision,
138
+ } from './ledger.mjs';
139
+ // Intent AI event sink , the append-only audit log of intent-ai-v1 events
140
+ export {
141
+ EVENT_LOG_SCHEMA, emptyEventLog, recordEvent, parseEventLog, serializeEventLog,
142
+ eventsFor, eventsOfType, timeline,
143
+ } from './ai-events.mjs';
144
+ // Verify a code change against its intent , the AI generate/verify loop gate
145
+ export { verifyDiff, VERIFY_DIFF_SCHEMA } from './verify-diff.mjs';
146
+ // MCP server , ThunderLang as a native tool for AI coding agents
147
+ export { startMcpServer, MCP_TOOLS } from './mcp.mjs';
148
+ // Runtime enforcement , compile intent into a guard that blocks forbidden actions + redacts secrets
149
+ export { buildGuard, compileGuard, guardSummary, GUARD_SCHEMA } from './guard.mjs';
150
+ // Prompt -> intent (deterministic half) , scaffold a rigorous draft + gap checklist from a brief
151
+ export { draftIntent, DRAFT_SCHEMA } from './draft.mjs';
152
+ // Style intent , brand/visual language as a governed Experience-profile extension
153
+ export {
154
+ analyzeStyle, styleDiagnostics, toDesignTokens, toCss, STYLE_SCHEMA, DESIGN_TOKENS_SCHEMA,
155
+ TOKEN_PATHS, BRAND_PATHS, STYLE_ADDRESS_SPACE, ACCESSIBILITY_TARGETS, MODE_VALUES,
156
+ ACCESSIBILITY_CLASSIFICATION,
157
+ } from './style.mjs';
158
+ export {
159
+ SCHEMA_VERSION, NODE_TYPES, RELATIONSHIP_TYPES, NODE_STATUSES,
160
+ intentGraphJsonSchema, DIAGNOSTIC_RULES, CORE_DIAGNOSTICS, ALL_DIAGNOSTICS,
161
+ RULE_PHASES, RULE_OWNERS, RULE_NAMESPACES, VERIFICATION_RULES, ruleNamespace,
162
+ } from './intent-schema.mjs';
163
+ // Intent IR (intent-ir-v1) , the shared ecosystem semantic representation (superset of the graph)
164
+ export {
165
+ IR_SCHEMA, IR_EMBEDS, IR_NODE_TYPES, IR_RELATIONSHIP_TYPES, PROVENANCE, FACTUAL_PROVENANCE,
166
+ isFactualProvenance, IR_CONFIDENCE, IR_CONFIDENCE_MEANING, confidenceFromClassification, SENSITIVITY,
167
+ RETENTION, REVIEW_STATUS, APPROVAL_STATUS, NODE_FIELDS, validateIR, graphToIR,
168
+ } from './intent-ir.mjs';
169
+
170
+ // Intent AI implementations (intent-ai-v1): state model, marker parser, hashing, manifest
171
+ export {
172
+ IMPLEMENTATION_STATES, RISK_LEVELS, HIGH_RISK, blocksProduction,
173
+ COMMENT_PREFIX, parseMarkers, renderMarker,
174
+ contractHash, implementationHash,
175
+ buildManifest, buildImplementationPrompt, MANIFEST_SCHEMA_VERSION, PROOF_CHECK_KEYS,
176
+ resolveState, productionGate, adoptRegion,
177
+ recordDecision, approvalFor, emptyApprovals, APPROVALS_SCHEMA_VERSION,
178
+ makeEvent, INTENT_AI_EVENTS,
179
+ } from './ai.mjs';
180
+
181
+ // Language Server (LSP over stdio) for editors
182
+ export { startLspServer } from "./lsp.mjs";
183
+
184
+ // Formatter , canonical .intent formatting (whitespace only)
185
+ export { formatSource, isFormatted } from "./format.mjs";
@@ -0,0 +1,210 @@
1
+ // ThunderLang IntelliSense providers (deterministic, no AI). Pure functions the
2
+ // CLI, the playground API, and a future LSP all share. The playground must NOT
3
+ // reimplement these; it renders what the compiler returns.
4
+
5
+ import { KNOWN_LENSES } from './parse.mjs';
6
+
7
+ export const SEMANTIC_TYPES = [
8
+ 'Email', 'Money(USD)', 'Currency', 'Url', 'UserId', 'AccountId', 'OrderId',
9
+ 'InvoiceId', 'PaymentId', 'Secret', 'Token', 'Jwt', 'IdempotencyKey', 'Date',
10
+ 'DateTime', 'Duration', 'Percentage', 'TraceId', 'CorrelationId',
11
+ ];
12
+
13
+ // Hover text for semantic types. Types not listed get a generic explanation.
14
+ const TYPE_INFO = {
15
+ Email: { description: 'A validated email address. Prefer this over a raw string so tools can reason about PII and format.', examples: ['user@company.com'] },
16
+ Money: { description: 'A monetary amount with a currency, for example Money(USD). Avoids float rounding bugs and ambiguous units.', examples: ['Money(USD) 100.00'] },
17
+ 'Money(USD)': { description: 'A monetary amount in US dollars. Prefer a typed money value over a number to avoid rounding and currency bugs.', examples: ['100.00'] },
18
+ Secret: { description: 'Sensitive value that must never be logged, returned to a client, or placed in events, proof, or AI context.', examples: ['paymentToken: Secret'] },
19
+ Token: { description: 'An opaque credential. Treat like a Secret: never log or return it.', examples: ['resetToken: Token'] },
20
+ Jwt: { description: 'A JSON Web Token credential. Sensitive; never log or expose it.', examples: [] },
21
+ IdempotencyKey: { description: 'A retry key. The same key must return the same result instead of creating a duplicate action.', examples: ['Use it to prevent duplicate invoices when checkout retries.'], relatedSuggestions: ['Add a duplicate prevention guarantee', 'Add a repeated-order verification test'] },
22
+ OrderId: { description: 'The stable identity of a placed order.', examples: ['A1'] },
23
+ InvoiceId: { description: 'The stable identity of an issued invoice.', examples: ['INV-1'] },
24
+ TraceId: { description: 'A distributed-tracing identifier used to correlate work across services.', examples: [] },
25
+ CorrelationId: { description: 'An identifier that correlates related requests or events.', examples: [] },
26
+ };
27
+
28
+ const LENS_INFO = {
29
+ pm: 'Business meaning for product and non-technical stakeholders.',
30
+ beginner: 'Plain-English explanation for someone new to programming.',
31
+ qa: 'How to test or verify this behavior.',
32
+ risk: 'What goes wrong if this fails.',
33
+ security: 'Privacy, secrets, auth, or data-exposure meaning.',
34
+ support: 'What support needs to know to help customers.',
35
+ reviewer: 'What a reviewer should check before approving.',
36
+ ops: 'What production signal or failure mode to watch.',
37
+ non_goal: 'What this mission intentionally does not do.',
38
+ term: 'Defines an unfamiliar term for readers.',
39
+ };
40
+
41
+ const BLOCK_KEYWORDS = ['goal', 'why', 'input', 'output', 'guarantees', 'never', 'verify', 'target', 'examples', 'note'];
42
+ const SENSITIVE = /payment token|secret|token|password|jwt|credential|ssn|pii|email/i;
43
+
44
+ const item = (id, label, insertText, detail, sortText = '050', kind = 'snippet') =>
45
+ ({ id, label, kind, detail, insertText, sortText, source: 'compiler', confidence: 'high' });
46
+
47
+ const noteItem = (lens) =>
48
+ item(`completion_note_${lens}`, `note ${lens}:`, `note ${lens}:\n \${1:${LENS_INFO[lens]}}`, LENS_INFO[lens], '010');
49
+
50
+ const typeItem = (t) =>
51
+ item(`completion_type_${t}`, t, t, `Semantic type: ${(TYPE_INFO[t]?.description || TYPE_INFO[t.replace(/\(.*/, '')]?.description || 'A semantic type.')}`, '020', 'type');
52
+
53
+ // Nearest enclosing top-level block above the cursor (by indentation).
54
+ function enclosingBlock(lines, idx) {
55
+ const curIndent = lines[idx].length - lines[idx].trimStart().length;
56
+ if (curIndent === 0) return null;
57
+ for (let i = idx; i >= 0; i--) {
58
+ const l = lines[i];
59
+ if (!l.trim()) continue;
60
+ const ind = l.length - l.trimStart().length;
61
+ if (ind < curIndent) return l.trim().split(/\s+/)[0].toLowerCase();
62
+ }
63
+ return null;
64
+ }
65
+
66
+ export function getCompletions(source, position = {}) {
67
+ const lines = source.split('\n');
68
+ const idx = Math.max(0, Math.min((position.line || 1) - 1, lines.length - 1));
69
+ const cur = lines[idx] ?? '';
70
+ const items = [];
71
+
72
+ const meaningful = lines.filter((l) => l.trim() && !l.trim().startsWith('#'));
73
+ if (meaningful.length === 0) {
74
+ items.push(item('completion_mission_starter', 'mission starter',
75
+ 'mission ${1:MissionName}\n note pm:\n ${2:Plain-English business meaning.}\n\n goal\n ${3:What should this accomplish?}\n\n why\n ${4:Why does this matter?}\n\n input\n ${5:field}: ${6:SemanticType}\n\n output\n ${7:result}: ${8:ResultType}\n\n guarantees\n ${9:Something that must always be true}\n\n never\n ${10:Something that must never happen}\n\n verify\n test ${11:expected behavior}\n',
76
+ 'Start a new mission with goal, why, input, output, guarantees, never, verify.', '001'));
77
+ return { items };
78
+ }
79
+
80
+ // Typing a note lens: `note ` or `note pa`
81
+ if (/(^|\s)note\s+\w*$/.test(cur)) {
82
+ for (const lens of KNOWN_LENSES) items.push(noteItem(lens));
83
+ return { items };
84
+ }
85
+
86
+ const ctx = enclosingBlock(lines, idx);
87
+
88
+ if (SENSITIVE.test(cur) || SENSITIVE.test(lines[idx - 1] || '')) {
89
+ items.push(item('completion_note_security', 'note security:',
90
+ 'note security:\n ${1:This value must not appear in logs, events, responses, proof artifacts, or AI context.}',
91
+ 'Flag sensitive data handling for security readers.', '005'));
92
+ }
93
+
94
+ if (ctx === 'input' || ctx === 'output') {
95
+ for (const t of SEMANTIC_TYPES) items.push(typeItem(t));
96
+ for (const l of ['beginner', 'pm', 'qa']) items.push(noteItem(l));
97
+ } else if (ctx === 'guarantees' || ctx === 'guarantee') {
98
+ for (const l of ['pm', 'risk', 'qa']) items.push(noteItem(l));
99
+ items.push(item('completion_verify', 'verify:', 'verify\n test ${1:expected behavior}', 'Add verification evidence.', '015'));
100
+ } else if (ctx === 'never') {
101
+ for (const l of ['security', 'risk', 'reviewer']) items.push(noteItem(l));
102
+ items.push(item('completion_verify', 'verify:', 'verify\n ${1:security scan}', 'Add verification evidence.', '015'));
103
+ } else {
104
+ for (const kw of BLOCK_KEYWORDS) {
105
+ if (kw === 'note') { items.push(noteItem('pm'), noteItem('beginner')); continue; }
106
+ items.push(item(`completion_block_${kw}`, `${kw}`, `${kw}\n \${1:...}`, `Add a ${kw} block.`, '030', 'keyword'));
107
+ }
108
+ }
109
+ return { items };
110
+ }
111
+
112
+ export function getHover(source, position = {}) {
113
+ const lines = source.split('\n');
114
+ const idx = Math.max(0, Math.min((position.line || 1) - 1, lines.length - 1));
115
+ const cur = lines[idx] ?? '';
116
+ const col = Math.max(0, (position.column || 1) - 1);
117
+
118
+ // Word under the cursor.
119
+ let s = col, e = col;
120
+ while (s > 0 && /[A-Za-z0-9_()]/.test(cur[s - 1])) s--;
121
+ while (e < cur.length && /[A-Za-z0-9_()]/.test(cur[e])) e++;
122
+ const word = cur.slice(s, e);
123
+ if (!word) return { hover: null };
124
+
125
+ // note lens hover
126
+ const noteMatch = cur.match(/^\s*note\s+([A-Za-z_]+)/);
127
+ if (noteMatch && (word === noteMatch[1] || word === 'note')) {
128
+ const lens = noteMatch[1];
129
+ return {
130
+ hover: {
131
+ target: lens, kind: 'note_lens', title: `note ${lens}`,
132
+ description: LENS_INFO[lens] || 'An IntentLens reader lens.',
133
+ examples: [], relatedSuggestions: [],
134
+ },
135
+ };
136
+ }
137
+
138
+ // semantic type hover (known type, or a PascalCase type-like identifier)
139
+ const base = word.replace(/\(.*\)/, '');
140
+ const isKnownType = SEMANTIC_TYPES.some((t) => t === word || t.replace(/\(.*/, '') === base);
141
+ const info = TYPE_INFO[word] || TYPE_INFO[base];
142
+ if (info || isKnownType || /^[A-Z][a-z]/.test(word)) {
143
+ return {
144
+ hover: {
145
+ target: word, kind: 'semantic_type', title: word,
146
+ description: info?.description || 'A semantic type. Prefer semantic types over raw string or number so tools can reason about meaning.',
147
+ examples: info?.examples || [],
148
+ relatedSuggestions: info?.relatedSuggestions || [],
149
+ },
150
+ };
151
+ }
152
+ return { hover: null };
153
+ }
154
+
155
+ // ── Autocorrect + code actions (deterministic, safety-graded) ────────────────
156
+ // Provably-safe header normalizations only. Aliases are limited to words that are
157
+ // NOT valid canonical keywords in any context (so, e.g., `inputs` is intentionally
158
+ // absent: it is the canonical sub-block of a decision and must not be touched).
159
+ const HEADER_ALIASES = { goals: 'goal', nevers: 'never' };
160
+ const TOP_HEADERS = new Set([
161
+ 'goal', 'why', 'requires', 'input', 'output', 'guarantees', 'never', 'constraints',
162
+ 'assumptions', 'risks', 'target', 'style', 'verify', 'errors', 'examples',
163
+ ]);
164
+
165
+ /**
166
+ * Apply only meaning-preserving textual fixes: rename a bare misspelled block header
167
+ * to its canonical form (`goals` -> `goal`, `nevers` -> `never`) and strip a stray
168
+ * trailing colon from a recognized top-level header (`goal:` -> `goal`). Operates on
169
+ * single-word header lines only, so attached forms and leaf values are never rewritten.
170
+ * Returns { fixed, changes }. Pure and browser-safe.
171
+ */
172
+ export function autocorrectSource(source) {
173
+ const lines = String(source).split('\n');
174
+ const changes = [];
175
+ const out = lines.map((line, i) => {
176
+ const m = line.match(/^(\s*)([A-Za-z]+)(:?)\s*$/);
177
+ if (!m) return line;
178
+ const [, indent, word, colon] = m;
179
+ const alias = HEADER_ALIASES[word.toLowerCase()];
180
+ const target = alias || word;
181
+ const isHeader = TOP_HEADERS.has(target.toLowerCase());
182
+ const dropColon = Boolean(colon) && isHeader;
183
+ if (target === word && !dropColon) return line;
184
+ const rule = alias ? 'header-alias' : 'strip-colon';
185
+ const fixed = `${indent}${target}`;
186
+ changes.push({ line: i + 1, from: line.trim(), to: fixed.trim(), rule, safety: 'safe' });
187
+ return fixed;
188
+ });
189
+ return { fixed: out.join('\n'), changes };
190
+ }
191
+
192
+ /**
193
+ * The code actions available for a source: the safe autocorrects, plus the quick-fixes
194
+ * the semantic diagnostics already carry (graded `reviewable` because they insert
195
+ * placeholder content a human should confirm). Diagnostics are passed in so this module
196
+ * stays browser-safe (no crypto import). Each action carries a safety level:
197
+ * safe | reviewable | meaning_change | blocked.
198
+ */
199
+ export function getCodeActions(source, diagnostics = []) {
200
+ const actions = [];
201
+ for (const c of autocorrectSource(source).changes) {
202
+ actions.push({ title: `Normalize "${c.from}" to "${c.to}"`, kind: 'autocorrect', safety: 'safe', line: c.line, rule: c.rule });
203
+ }
204
+ for (const d of diagnostics) {
205
+ for (const f of d.fix || []) {
206
+ actions.push({ title: f.label, kind: 'quickfix', safety: 'reviewable', code: d.code, line: d.line ?? null, insert: f.insert, block: f.block });
207
+ }
208
+ }
209
+ return actions;
210
+ }
@@ -0,0 +1,77 @@
1
+ // Intent Atlas (founder directive #4). The navigable, expandable, searchable map of a
2
+ // whole software system, built OVER the canonical Intent Graph (intent-graph-v1) , NOT a
3
+ // fork. Missions are the entry points (progressive disclosure: you never start from
4
+ // files/folders). Deterministic; no AI (non-negotiable #1). Pure (no Node deps).
5
+ //
6
+ // Mission Atlas (focused per-mission map) is `mission-index-v1` / buildMissionIndex.
7
+ // This assembles many mission graphs into the whole-system Atlas.
8
+
9
+ export const ATLAS_SCHEMA = 'intent-atlas-v1';
10
+
11
+ /**
12
+ * Assemble many Intent Graphs (one per .intent) into one whole-system Atlas.
13
+ * @param {Array<{schema, missionId, nodes, relationships}>} graphs
14
+ * @param {{product?: string}} [opts]
15
+ */
16
+ export function buildAtlas(graphs, opts = {}) {
17
+ const nodes = new Map();
18
+ const relationships = [];
19
+ const missions = [];
20
+ for (const g of graphs || []) {
21
+ const m = g.nodes.find((n) => n.id === g.missionId);
22
+ missions.push({ id: g.missionId, title: (m && m.title) || g.missionId });
23
+ for (const n of g.nodes) if (!nodes.has(n.id)) nodes.set(n.id, n);
24
+ for (const r of g.relationships) relationships.push(r);
25
+ }
26
+ const nodeList = [...nodes.values()].sort((a, b) => a.id.localeCompare(b.id));
27
+ const byType = {};
28
+ for (const n of nodeList) byType[n.type] = (byType[n.type] || 0) + 1;
29
+ missions.sort((a, b) => a.id.localeCompare(b.id));
30
+ return {
31
+ schema: ATLAS_SCHEMA,
32
+ product: opts.product || null,
33
+ // Progressive disclosure starts here: the overview is missions + type counts, not nodes.
34
+ overview: { missions: missions.length, nodes: nodeList.length, relationships: relationships.length, byType },
35
+ missions,
36
+ nodes: nodeList,
37
+ relationships,
38
+ };
39
+ }
40
+
41
+ /** Look up one node by id. */
42
+ export function atlasNode(atlas, id) {
43
+ return atlas.nodes.find((n) => n.id === id) || null;
44
+ }
45
+
46
+ /**
47
+ * Expand a node to its immediate neighbors (outbound + inbound edges). This is the
48
+ * navigation primitive: never dump the whole graph, expand one node at a time.
49
+ */
50
+ export function expandNode(atlas, id) {
51
+ const node = atlasNode(atlas, id);
52
+ if (!node) return null;
53
+ const out = atlas.relationships.filter((r) => r.from === id).map((r) => ({ rel: r.type, node: atlasNode(atlas, r.to) || { id: r.to } }));
54
+ const inbound = atlas.relationships.filter((r) => r.to === id).map((r) => ({ rel: r.type, node: atlasNode(atlas, r.from) || { id: r.from } }));
55
+ return { node, out, inbound };
56
+ }
57
+
58
+ /**
59
+ * Deterministic search over the Atlas (exact + substring over id/title/description),
60
+ * with optional type filter. No AI. Ranking: exact-title, then id-prefix, then
61
+ * substring; stable tiebreak by type + id.
62
+ */
63
+ export function searchAtlas(atlas, query, opts = {}) {
64
+ const q = String(query || '').toLowerCase().trim();
65
+ if (!q) return [];
66
+ const score = (n) => {
67
+ const title = (n.title || '').toLowerCase();
68
+ const id = n.id.toLowerCase();
69
+ if (title === q || id === q) return 0;
70
+ if (id.startsWith(q) || title.startsWith(q)) return 1;
71
+ return 2;
72
+ };
73
+ let hits = atlas.nodes.filter((n) => `${n.id} ${n.title || ''} ${n.description || ''}`.toLowerCase().includes(q));
74
+ if (opts.type) hits = hits.filter((n) => n.type === opts.type);
75
+ hits.sort((a, b) => score(a) - score(b) || a.type.localeCompare(b.type) || a.id.localeCompare(b.id));
76
+ return hits.slice(0, opts.limit || 25);
77
+ }