norn-cli 4.3.2 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6653,7 +6653,7 @@ function ensureNornCacheDir(rootPath) {
6653
6653
  }
6654
6654
 
6655
6655
  // src/knowledge/knowledgeIndex.ts
6656
- var KNOWLEDGE_SCHEMA_VERSION = 2;
6656
+ var KNOWLEDGE_SCHEMA_VERSION = 3;
6657
6657
  var BUSY_TIMEOUT_MS = 5e3;
6658
6658
  var SCHEMA = `
6659
6659
  CREATE TABLE IF NOT EXISTS control (
@@ -6717,6 +6717,7 @@ CREATE TABLE IF NOT EXISTS snapshot_occurrences (
6717
6717
  topic_hash TEXT NOT NULL REFERENCES topics(topic_hash),
6718
6718
  authored_type TEXT,
6719
6719
  authored_use_when TEXT,
6720
+ environment_import TEXT,
6720
6721
  effective_type TEXT NOT NULL,
6721
6722
  start_line INTEGER NOT NULL,
6722
6723
  end_line INTEGER NOT NULL,
@@ -6907,7 +6908,7 @@ var KnowledgeIndex = class {
6907
6908
  fs2.mkdirSync(path2.dirname(this.databasePath), { recursive: true, mode: 448 });
6908
6909
  let database = new import_node_sqlite.DatabaseSync(this.databasePath);
6909
6910
  const version = currentSchemaVersion(database);
6910
- if (version !== 0 && version !== 1 && version !== KNOWLEDGE_SCHEMA_VERSION) {
6911
+ if (version !== 0 && version !== 1 && version !== 2 && version !== KNOWLEDGE_SCHEMA_VERSION) {
6911
6912
  database.close();
6912
6913
  if (options.rejectUnsupportedSchema) {
6913
6914
  throw new Error(`Unsupported knowledge index schema ${version}; expected ${KNOWLEDGE_SCHEMA_VERSION}.`);
@@ -6918,6 +6919,10 @@ var KnowledgeIndex = class {
6918
6919
  this.database = database;
6919
6920
  this.database.exec(`PRAGMA journal_mode = WAL; PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}; PRAGMA foreign_keys = ON;`);
6920
6921
  this.database.exec(SCHEMA);
6922
+ const occurrenceColumns = this.database.prepare("PRAGMA table_info(snapshot_occurrences)").all();
6923
+ if (!occurrenceColumns.some((column) => column.name === "environment_import")) {
6924
+ this.database.exec("ALTER TABLE snapshot_occurrences ADD COLUMN environment_import TEXT");
6925
+ }
6921
6926
  this.database.exec(`PRAGMA user_version = ${KNOWLEDGE_SCHEMA_VERSION}`);
6922
6927
  try {
6923
6928
  fs2.chmodSync(this.databasePath, 384);
@@ -6994,6 +6999,9 @@ var KnowledgeIndex = class {
6994
6999
  if (row.authored_use_when) {
6995
7000
  metadata.useWhen = { origin: "authored", value: String(row.authored_use_when) };
6996
7001
  }
7002
+ if (row.environment_import) {
7003
+ metadata.import = { origin: "authored", value: String(row.environment_import) };
7004
+ }
6997
7005
  return {
6998
7006
  declarationId: String(row.declaration_id),
6999
7007
  declarationPath: String(row.declaration_path),
@@ -7252,7 +7260,7 @@ function queryKnowledgeEditorFacts(query, options = {}) {
7252
7260
  for (const context of query.contexts) {
7253
7261
  const rows = index.all(`
7254
7262
  SELECT topic_hash, domain_path, item_key, ordinal, heading_text, effective_type,
7255
- authored_type, authored_use_when
7263
+ authored_type, authored_use_when, environment_import
7256
7264
  FROM snapshot_occurrences
7257
7265
  WHERE snapshot_id = ? AND declaration_id = ? AND source_id = ? AND item_key = ?
7258
7266
  ORDER BY ordinal
@@ -7265,6 +7273,9 @@ function queryKnowledgeEditorFacts(query, options = {}) {
7265
7273
  if (row.authored_use_when !== null) {
7266
7274
  metadata.useWhen = { origin: "authored", value: String(row.authored_use_when) };
7267
7275
  }
7276
+ if (row.environment_import !== null) {
7277
+ metadata.import = { origin: "authored", value: String(row.environment_import) };
7278
+ }
7268
7279
  const topicHash = String(row.topic_hash);
7269
7280
  const bucket = occurrencesByHash.get(topicHash) ?? [];
7270
7281
  bucket.push({
@@ -7320,6 +7331,12 @@ function queryKnowledgeEditorFacts(query, options = {}) {
7320
7331
  }
7321
7332
  }
7322
7333
 
7334
+ // src/agents/agentSidecarPath.ts
7335
+ function isAgentSidecarPath(filePath) {
7336
+ const lowerPath = filePath.toLowerCase();
7337
+ return lowerPath.endsWith(".nornagent") || lowerPath.endsWith(".nornai");
7338
+ }
7339
+
7323
7340
  // src/knowledge/knowledgeIndexBuilder.ts
7324
7341
  var fs8 = __toESM(require("fs"));
7325
7342
  var path10 = __toESM(require("path"));
@@ -7878,6 +7895,7 @@ function parseNornAgentFile(content, sourcePath) {
7878
7895
  let pendingMcp;
7879
7896
  let pendingKnowledge;
7880
7897
  let sawAgent = false;
7898
+ let sawMcpDeclaration = false;
7881
7899
  const reportBareMcpAlias = (mcp) => {
7882
7900
  errors.push(parseError(
7883
7901
  mcp.lineNumber,
@@ -8446,6 +8464,7 @@ end model`,
8446
8464
  }
8447
8465
  const mcpMatch = trimmed.match(new RegExp(`^mcp\\s+(${IDENTIFIER})$`, "i"));
8448
8466
  if (mcpMatch) {
8467
+ sawMcpDeclaration = true;
8449
8468
  const alias = mcpMatch[1];
8450
8469
  let invalid = false;
8451
8470
  if (sawAgent) {
@@ -8838,11 +8857,11 @@ end model`,
8838
8857
  "agent-knowledge-end-missing"
8839
8858
  ));
8840
8859
  }
8841
- if (models.size === 0) {
8842
- errors.push(parseError(0, "A .nornagent file must declare at least one model block.", "agent-model-required"));
8860
+ if (!sawMcpDeclaration && models.size === 0) {
8861
+ errors.push(parseError(0, "An agent sidecar must declare at least one model block.", "agent-model-required"));
8843
8862
  }
8844
- if (agentNames.size === 0) {
8845
- errors.push(parseError(0, "A .nornagent file must declare at least one agent.", "agent-required"));
8863
+ if (!sawMcpDeclaration && agentNames.size === 0) {
8864
+ errors.push(parseError(0, "An agent sidecar must declare at least one agent.", "agent-required"));
8846
8865
  }
8847
8866
  const localScope = new Map(agents.map((agent) => [agent.name.toLowerCase(), agent]));
8848
8867
  for (const validationError of validateAgentScope(localScope, { checkUnknownReferences: false })) {
@@ -8929,10 +8948,26 @@ function parseMetaBlock(lines, startLine, endLine, diagnostics) {
8929
8948
  } else {
8930
8949
  metadata.useWhen = authored(value);
8931
8950
  }
8951
+ } else if (key === "import") {
8952
+ if (metadata.import) {
8953
+ diagnostics.push({
8954
+ lineNumber: startLine + offset,
8955
+ message: "norn:meta declares import more than once.",
8956
+ code: "knowledge-meta-import-duplicate"
8957
+ });
8958
+ } else if (!/^(["']).*\.nornenv\1$/.test(value)) {
8959
+ diagnostics.push({
8960
+ lineNumber: startLine + offset,
8961
+ message: "The environment import must be a quoted .nornenv path.",
8962
+ code: "knowledge-meta-import-invalid"
8963
+ });
8964
+ } else {
8965
+ metadata.import = authored(value.slice(1, -1));
8966
+ }
8932
8967
  } else {
8933
8968
  diagnostics.push({
8934
8969
  lineNumber: startLine + offset,
8935
- message: `Unknown norn:meta key '${match[1]}'. Expected type or use_when.`,
8970
+ message: `Unknown norn:meta key '${match[1]}'. Expected type, use_when, or import.`,
8936
8971
  code: "knowledge-meta-key-unknown"
8937
8972
  });
8938
8973
  }
@@ -8985,6 +9020,15 @@ function mergeMetadata(target, incoming, lineNumber, diagnostics) {
8985
9020
  } else if (incoming.useWhen) {
8986
9021
  target.useWhen = incoming.useWhen;
8987
9022
  }
9023
+ if (target.import && incoming.import) {
9024
+ diagnostics.push({
9025
+ lineNumber,
9026
+ message: "A topic may have only one environment import.",
9027
+ code: "knowledge-meta-import-duplicate"
9028
+ });
9029
+ } else if (incoming.import) {
9030
+ target.import = incoming.import;
9031
+ }
8988
9032
  }
8989
9033
  function firstContentLine(lines, from, to, metaByLine) {
8990
9034
  for (let line = from; line <= to; line++) {
@@ -9077,6 +9121,14 @@ function extractKnowledgeTopics(text, filePath, headingDepth = 2) {
9077
9121
  const preambleEnd = firstBoundary - 1;
9078
9122
  const preamble = metadataFor(h1Line >= 0 ? h1Line : -1, preambleEnd);
9079
9123
  const preambleMetadata = preamble.metadata;
9124
+ if (preambleMetadata.import && h1Line < 0) {
9125
+ diagnostics.push({
9126
+ lineNumber: preamble.block?.startLine ?? 0,
9127
+ message: "An environment import must sit under a # title.",
9128
+ code: "knowledge-meta-import-placement"
9129
+ });
9130
+ delete preambleMetadata.import;
9131
+ }
9080
9132
  const preambleBody = bodyBetween(lines, 0, preambleEnd, removedLines);
9081
9133
  if (h1Text || preambleBody) {
9082
9134
  topics.push({
@@ -9100,6 +9152,17 @@ function extractKnowledgeTopics(text, filePath, headingDepth = 2) {
9100
9152
  const endLine = (boundaries[index + 1] ?? lines.length) - 1;
9101
9153
  const headingText = headingPattern.exec(lines[headingLine])?.[1].trim() ?? "";
9102
9154
  const { metadata, block } = metadataFor(headingLine, endLine);
9155
+ if (metadata.import) {
9156
+ diagnostics.push({
9157
+ lineNumber: block?.startLine ?? headingLine,
9158
+ message: "An environment import belongs under the # title, not a section.",
9159
+ code: "knowledge-meta-import-placement"
9160
+ });
9161
+ delete metadata.import;
9162
+ }
9163
+ if (preambleMetadata.import) {
9164
+ metadata.import = preambleMetadata.import;
9165
+ }
9103
9166
  const body = bodyBetween(lines, headingLine + 1, endLine, removedLines);
9104
9167
  let excluded = fileExcluded;
9105
9168
  let exclusionReason = fileExcluded ? "file marker <!-- norn:exclude -->" : void 0;
@@ -9152,7 +9215,7 @@ function extractKnowledgeTopics(text, filePath, headingDepth = 2) {
9152
9215
 
9153
9216
  // src/knowledge/knowledgeBuilder.ts
9154
9217
  var DEFAULT_EXTENSIONS = [".md"];
9155
- var KNOWLEDGE_MARKDOWN_ANALYZER_VERSION = 2;
9218
+ var KNOWLEDGE_MARKDOWN_ANALYZER_VERSION = 3;
9156
9219
  function normalizeRelative(filePath) {
9157
9220
  return filePath.split(path7.sep).join("/").replace(/^\.\//, "");
9158
9221
  }
@@ -10413,7 +10476,7 @@ function walkSidecars(directory) {
10413
10476
  if (entry.isDirectory()) {
10414
10477
  return walkSidecars(candidate);
10415
10478
  }
10416
- return entry.isFile() && entry.name.toLowerCase().endsWith(".nornagent") ? [candidate] : [];
10479
+ return entry.isFile() && isAgentSidecarPath(entry.name) ? [candidate] : [];
10417
10480
  });
10418
10481
  }
10419
10482
  function discoverKnowledgeDeclarations(projectRoot) {
@@ -10715,8 +10778,8 @@ function commitSnapshot(index, projectRoot, snapshotId, manifestJson, builtAt, d
10715
10778
  INSERT OR IGNORE INTO snapshot_occurrences(
10716
10779
  snapshot_id, declaration_id, declaration_path, declaration_alias, source_id, source_path,
10717
10780
  item_key, domain_path, heading_text, ordinal, topic_hash, authored_type, authored_use_when,
10718
- effective_type, start_line, end_line
10719
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10781
+ environment_import, effective_type, start_line, end_line
10782
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10720
10783
  `,
10721
10784
  snapshotId,
10722
10785
  occurrence.declarationId,
@@ -10731,6 +10794,7 @@ function commitSnapshot(index, projectRoot, snapshotId, manifestJson, builtAt, d
10731
10794
  occurrence.topicHash,
10732
10795
  occurrence.metadata.type?.value ?? null,
10733
10796
  occurrence.metadata.useWhen?.value ?? null,
10797
+ occurrence.metadata.import?.value ?? null,
10734
10798
  occurrence.effectiveType,
10735
10799
  occurrence.startLine,
10736
10800
  occurrence.endLine
@@ -10915,7 +10979,8 @@ function buildKnowledgeIndex(startPath, options = {}) {
10915
10979
  ordinal: occurrence.ordinal,
10916
10980
  topicHash: occurrence.topicHash,
10917
10981
  authoredType: occurrence.metadata.type?.value ?? null,
10918
- authoredUseWhen: occurrence.metadata.useWhen?.value ?? null
10982
+ authoredUseWhen: occurrence.metadata.useWhen?.value ?? null,
10983
+ authoredImport: occurrence.metadata.import?.value ?? null
10919
10984
  })),
10920
10985
  projectInputs: projectInputs.map((input) => ({ path: input.projectPath, contentHash: input.contentHash })).sort((left, right) => left.path.localeCompare(right.path)),
10921
10986
  analyzers: [
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "norn-cli",
3
3
  "displayName": "Norn",
4
- "description": "Version-controlled API and database tests. Author in VS Code, then run the same files from the CLI and CI.",
5
- "version": "4.3.2",
4
+ "description": "Build version-controlled agentic flows in VS Code and run them from the CLI or CI.",
5
+ "version": "4.5.0",
6
6
  "publisher": "Norn-PeterKrustanov",
7
7
  "author": {
8
8
  "name": "Peter Krastanov"
@@ -47,7 +47,9 @@
47
47
  "onLanguage:nornenv",
48
48
  "onLanguage:nornk8s",
49
49
  "onLanguage:nornagent",
50
+ "onLanguage:markdown",
50
51
  "workspaceContains:**/*.nornagent",
52
+ "workspaceContains:**/*.nornai",
51
53
  "workspaceContains:**/norn.config.json",
52
54
  "onChatParticipant:norn.chat",
53
55
  "onView:norn.sidebarHome",
@@ -55,12 +57,12 @@
55
57
  "onCommand:norn.importPostmanEnvironment",
56
58
  "onCommand:norn.createStarterCatalog",
57
59
  "onCommand:norn.createSqlStarterFiles",
58
- "onCommand:norn.createMcpStarterConfig",
59
60
  "onCommand:norn.openTerminal",
60
61
  "onCommand:norn.terminal.focusRawTerminal",
61
62
  "onCommand:norn.debugSequence",
62
63
  "onCommand:norn.debug.runToNextContractFailure",
63
64
  "onCommand:norn.showAgentGraph",
65
+ "onCommand:norn.chatWithAgent",
64
66
  "onCommand:norn.showAgentRunResults",
65
67
  "onDebugResolve:norn"
66
68
  ],
@@ -93,6 +95,11 @@
93
95
  "title": "Show Agent Graph",
94
96
  "category": "Norn"
95
97
  },
98
+ {
99
+ "command": "norn.chatWithAgent",
100
+ "title": "Chat With Agent",
101
+ "category": "Norn"
102
+ },
96
103
  {
97
104
  "command": "norn.showAgentRunResults",
98
105
  "title": "Show Agent Run Results",
@@ -192,11 +199,6 @@
192
199
  "title": "Open Norn Starter Catalogue",
193
200
  "category": "Norn"
194
201
  },
195
- {
196
- "command": "norn.createMcpStarterConfig",
197
- "title": "Create MCP Starter Config",
198
- "category": "Norn"
199
- },
200
202
  {
201
203
  "command": "norn.encryptSecretAtLine",
202
204
  "title": "Encrypt Secret At Line",
@@ -266,11 +268,6 @@
266
268
  "command": "norn.createStarterCatalog",
267
269
  "when": "explorerResourceIsFolder",
268
270
  "group": "norn@1"
269
- },
270
- {
271
- "command": "norn.createMcpStarterConfig",
272
- "when": "explorerResourceIsFolder",
273
- "group": "norn@2"
274
271
  }
275
272
  ],
276
273
  "debug/toolBar": [
@@ -369,7 +366,8 @@
369
366
  "nornagent"
370
367
  ],
371
368
  "extensions": [
372
- ".nornagent"
369
+ ".nornagent",
370
+ ".nornai"
373
371
  ],
374
372
  "icon": {
375
373
  "light": "./images/nornagent-icon.svg",
@@ -641,7 +639,7 @@
641
639
  "test:regression": "node ./tests/test-scripts/run-regression.js",
642
640
  "test:prerelease": "npm test && npm run test:regression",
643
641
  "publish:npm": "node -e \"const fs=require('fs');const p=require('./package.json');p.name='norn-cli';fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')\" && npm publish; exit_code=$?; node -e \"const fs=require('fs');const p=require('./package.json');p.name='norn';fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')\"; exit $exit_code",
644
- "publish:vsce": "vsce publish",
642
+ "publish:vsce": "node ./scripts/publish-vsce.mjs",
645
643
  "publish:all": "npm run publish:npm && npm run publish:vsce"
646
644
  },
647
645
  "devDependencies": {
@@ -486,7 +486,7 @@
486
486
  "agentsSection": {
487
487
  "type": "object",
488
488
  "additionalProperties": false,
489
- "description": "Global recording behavior and global/provider-specific limits for agent runs. Per-agent .nornagent directives override limit values.",
489
+ "description": "Global recording behavior and global/provider-specific limits for agent runs. Per-agent .nornai or .nornagent directives override limit values.",
490
490
  "properties": {
491
491
  "_comment": {
492
492
  "$ref": "#/definitions/comment",