@descent-vtt/spec-harness 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +85 -0
  4. package/README.md +273 -24
  5. package/dist/audit.d.ts +71 -1
  6. package/dist/audit.js +140 -17
  7. package/dist/audit.js.map +1 -1
  8. package/dist/branch.d.ts +13 -0
  9. package/dist/branch.js +19 -0
  10. package/dist/branch.js.map +1 -1
  11. package/dist/cli.d.ts +1 -1
  12. package/dist/cli.js +51 -13
  13. package/dist/cli.js.map +1 -1
  14. package/dist/commands.js +11 -4
  15. package/dist/commands.js.map +1 -1
  16. package/dist/config.js +3 -1
  17. package/dist/config.js.map +1 -1
  18. package/dist/configure.d.ts +50 -3
  19. package/dist/configure.js +78 -5
  20. package/dist/configure.js.map +1 -1
  21. package/dist/context.d.ts +10 -5
  22. package/dist/context.js +54 -19
  23. package/dist/context.js.map +1 -1
  24. package/dist/document.d.ts +2 -1
  25. package/dist/document.js.map +1 -1
  26. package/dist/formats.d.ts +31 -0
  27. package/dist/formats.js +127 -0
  28. package/dist/formats.js.map +1 -0
  29. package/dist/git.d.ts +6 -0
  30. package/dist/git.js +8 -0
  31. package/dist/git.js.map +1 -1
  32. package/dist/guard.d.ts +8 -0
  33. package/dist/guard.js +19 -1
  34. package/dist/guard.js.map +1 -1
  35. package/dist/host.d.ts +14 -0
  36. package/dist/host.js +50 -0
  37. package/dist/host.js.map +1 -0
  38. package/dist/index.d.ts +4 -2
  39. package/dist/index.js +2 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/manifests.d.ts +8 -1
  42. package/dist/manifests.js +16 -6
  43. package/dist/manifests.js.map +1 -1
  44. package/dist/premises.d.ts +3 -0
  45. package/dist/premises.js +43 -13
  46. package/dist/premises.js.map +1 -1
  47. package/dist/reader.js +10 -2
  48. package/dist/reader.js.map +1 -1
  49. package/dist/round.d.ts +8 -0
  50. package/dist/round.js +73 -22
  51. package/dist/round.js.map +1 -1
  52. package/dist/server.js +9 -3
  53. package/dist/server.js.map +1 -1
  54. package/dist/setup.d.ts +10 -0
  55. package/dist/setup.js +68 -32
  56. package/dist/setup.js.map +1 -1
  57. package/dist/signers.d.ts +43 -0
  58. package/dist/signers.js +105 -0
  59. package/dist/signers.js.map +1 -0
  60. package/dist/types.d.ts +7 -0
  61. package/dist/types.js.map +1 -1
  62. package/dist/vendor/spec-core/markdown/frontmatter.d.ts +11 -1
  63. package/dist/vendor/spec-core/markdown/frontmatter.js +20 -3
  64. package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -1
  65. package/dist/vendor/spec-core/pattern/glob.d.ts +85 -0
  66. package/dist/vendor/spec-core/pattern/glob.js +207 -17
  67. package/dist/vendor/spec-core/pattern/glob.js.map +1 -1
  68. package/dist/vendor/spec-core/pattern/index.d.ts +2 -2
  69. package/dist/vendor/spec-core/pattern/index.js +1 -1
  70. package/dist/vendor/spec-core/pattern/index.js.map +1 -1
  71. package/dist/vendor/spec-core/text/index.d.ts +1 -0
  72. package/dist/vendor/spec-core/text/index.js +1 -0
  73. package/dist/vendor/spec-core/text/index.js.map +1 -1
  74. package/dist/vendor/spec-core/text/width.d.ts +30 -0
  75. package/dist/vendor/spec-core/text/width.js +113 -0
  76. package/dist/vendor/spec-core/text/width.js.map +1 -0
  77. package/dist/versions.d.ts +27 -1
  78. package/dist/versions.js +23 -1
  79. package/dist/versions.js.map +1 -1
  80. package/dist/workspace.d.ts +6 -1
  81. package/dist/workspace.js +17 -6
  82. package/dist/workspace.js.map +1 -1
  83. package/package.json +1 -1
  84. package/skills/close-round/SKILL.md +4 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAI5C,qGAAqG;AACrG,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAE5D,8EAA8E;AAC9E,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,GAAiD,EAAE,QAAQ,GAAoB,OAAO,CAAC,QAAQ;IAClI,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,QAAQ,KAAK,OAAO,CAAC;IACrC,MAAM,UAAU,GAAG,OAAO;QACxB,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,KAAK,EAAE,CAAC;QACrG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9D,IAAI,SAAS,KAAK,EAAE;YAAE,SAAS;QAC/B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YAClE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE;gBAAE,OAAO,IAAI,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAiD,EAAE,QAAQ,GAAoB,OAAO,CAAC,QAAQ;IACjI,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC7C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAC9D,IAAI,QAAQ,KAAK,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,OAAO,EAAE,OAAO,EAAE,qBAAqB,IAAI,oFAAoF,EAAE,CAAC;IACpI,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACtH,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,sBAAsB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;;gBACvG,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * The edge where the harness meets a program that is neither git nor a\n * sibling: Claude Code, asked which release it is.\n *\n * Found on `PATH` and started without a shell, as every program the harness\n * runs is (ADR-0002). On Windows a program is an `.exe` or a `.com`; a\n * `claude.cmd` shim, as npm installs one, can be started only by a shell, so\n * it is named rather than run, and its release cannot be told.\n */\n\nimport { execFile } from 'node:child_process';\nimport { existsSync, statSync } from 'node:fs';\nimport { delimiter, join } from 'node:path';\n\nimport type { ClaudeCodeAnswer } from './versions.js';\n\n/** What Windows runs for a name with no extension, in order; the first two alone without a shell. */\nconst WINDOWS_EXTENSIONS = ['.COM', '.EXE', '.BAT', '.CMD'];\n\n/** The file `name` names on `PATH` as the environment gives it, or `null`. */\nexport function onPath(name: string, env: Readonly<Record<string, string | undefined>>, platform: NodeJS.Platform = process.platform): string | null {\n const path = env['PATH'] ?? env['Path'] ?? '';\n const windows = platform === 'win32';\n const extensions = windows\n ? (env['PATHEXT'] ?? WINDOWS_EXTENSIONS.join(';')).split(';').filter((extension) => extension !== '')\n : [''];\n for (const directory of path.split(windows ? ';' : delimiter)) {\n if (directory === '') continue;\n for (const extension of extensions) {\n const file = join(directory, `${name}${extension.toLowerCase()}`);\n if (existsSync(file) && statSync(file).isFile()) return file;\n }\n }\n return null;\n}\n\n/** `claude --version`, as the Claude Code on `PATH` answers it, or why it cannot. */\nexport async function claudeVersion(env: Readonly<Record<string, string | undefined>>, platform: NodeJS.Platform = process.platform): Promise<ClaudeCodeAnswer> {\n const file = onPath('claude', env, platform);\n if (file === null) return { missing: 'no claude is on PATH' };\n if (platform === 'win32' && !/\\.(?:exe|com)$/i.test(file)) {\n return { missing: `claude on PATH is ${file}, a script that cannot be started without a shell, which spec-harness does not use` };\n }\n return new Promise((resolve) => {\n execFile(file, ['--version'], { encoding: 'utf8', windowsHide: true, timeout: 15_000, shell: false }, (error, stdout) => {\n if (error !== null) resolve({ missing: `${file} --version failed: ${error.message.split('\\n')[0] ?? ''}` });\n else resolve({ output: stdout });\n });\n });\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -4,8 +4,8 @@
4
4
  */
5
5
  export { main, run, EXIT_ERROR, EXIT_FAILED, EXIT_OK, UsageError, version } from './cli.js';
6
6
  export type { CliIO } from './cli.js';
7
- export { audit } from './audit.js';
8
- export type { AssertionOutcome, AuditInput, AuditReport } from './audit.js';
7
+ export { audit, describeMeasured } from './audit.js';
8
+ export type { AssertionOutcome, AuditInput, AuditReport, Measured, UnreadableAssertion } from './audit.js';
9
9
  export { briefIdFromBranch, idFromBranch, sameId, templateError } from './branch.js';
10
10
  export { findActive, parseBriefList, SiblingOutputError, SUPPORTED_SCHEMA_VERSIONS } from './briefs.js';
11
11
  export type { ActiveBrief, ActiveSource } from './briefs.js';
@@ -13,6 +13,8 @@ export { ConfigError, CONFIG_FILE, DEFAULT_CONFIG, parseConfig, SIBLINGS } from
13
13
  export type { HarnessConfig, OutOfScope, SiblingName } from './config.js';
14
14
  export { renderContext } from './context.js';
15
15
  export type { CitedDocument, ContextInput, ContextPacket, RuleInForce, RulingInForce, UnreadablePattern, UnreadableRulingPath } from './context.js';
16
+ export { formatFindings, formatGithub, formatGitlab, formatSarif } from './formats.js';
17
+ export type { FindingFormat, FormatOptions } from './formats.js';
16
18
  export { decide } from './guard.js';
17
19
  export type { Decision, GuardInput, Reason, Verdict, VerifiedRuling } from './guard.js';
18
20
  export { claudeResponse, gitResponse, parseClaudeHook, WRITING_TOOLS } from './hooks.js';
package/dist/index.js CHANGED
@@ -3,11 +3,12 @@
3
3
  * another kind - a CI job, an orchestrator - can ask the same questions.
4
4
  */
5
5
  export { main, run, EXIT_ERROR, EXIT_FAILED, EXIT_OK, UsageError, version } from './cli.js';
6
- export { audit } from './audit.js';
6
+ export { audit, describeMeasured } from './audit.js';
7
7
  export { briefIdFromBranch, idFromBranch, sameId, templateError } from './branch.js';
8
8
  export { findActive, parseBriefList, SiblingOutputError, SUPPORTED_SCHEMA_VERSIONS } from './briefs.js';
9
9
  export { ConfigError, CONFIG_FILE, DEFAULT_CONFIG, parseConfig, SIBLINGS } from './config.js';
10
10
  export { renderContext } from './context.js';
11
+ export { formatFindings, formatGithub, formatGitlab, formatSarif } from './formats.js';
11
12
  export { decide } from './guard.js';
12
13
  export { claudeResponse, gitResponse, parseClaudeHook, WRITING_TOOLS } from './hooks.js';
13
14
  export { readJUnit } from './junit.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAG5F,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE9F,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzF,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAE7G,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEjG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1H,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["/**\n * Programmatic API. The command line is a thin shell over these; a harness of\n * another kind - a CI job, an orchestrator - can ask the same questions.\n */\n\nexport { main, run, EXIT_ERROR, EXIT_FAILED, EXIT_OK, UsageError, version } from './cli.js';\nexport type { CliIO } from './cli.js';\n\nexport { audit } from './audit.js';\nexport type { AssertionOutcome, AuditInput, AuditReport } from './audit.js';\nexport { briefIdFromBranch, idFromBranch, sameId, templateError } from './branch.js';\nexport { findActive, parseBriefList, SiblingOutputError, SUPPORTED_SCHEMA_VERSIONS } from './briefs.js';\nexport type { ActiveBrief, ActiveSource } from './briefs.js';\nexport { ConfigError, CONFIG_FILE, DEFAULT_CONFIG, parseConfig, SIBLINGS } from './config.js';\nexport type { HarnessConfig, OutOfScope, SiblingName } from './config.js';\nexport { renderContext } from './context.js';\nexport type { CitedDocument, ContextInput, ContextPacket, RuleInForce, RulingInForce, UnreadablePattern, UnreadableRulingPath } from './context.js';\nexport { decide } from './guard.js';\nexport type { Decision, GuardInput, Reason, Verdict, VerifiedRuling } from './guard.js';\nexport { claudeResponse, gitResponse, parseClaudeHook, WRITING_TOOLS } from './hooks.js';\nexport type { HookRequest, HookResponse } from './hooks.js';\nexport { readJUnit } from './junit.js';\nexport type { JUnitCase, JUnitRead } from './junit.js';\nexport { diffManifest, ecosystemOf, manifestMatcher, readManifest, readManifestNames } from './manifests.js';\nexport type { Dependency, DependencyChange, Ecosystem, ManifestNames } from './manifests.js';\nexport { classify, readProbes, renderEvidence, verdictOf } from './probe.js';\nexport type { Classified, CodeBlock, ProbeFile, ProbeResult, ProbeRun, ProbeSet, ProbeSpec } from './probe.js';\nexport { createReader } from './reader.js';\nexport { addRulingRow, nextId, pathsOf, readRulings, renderMemo, renderRow } from './rulings.js';\nexport type { EscalationOption, EscalationRequest, RulingRow, TableView } from './rulings.js';\nexport { buildContext, checkPaths, checkRulings, raiseEscalation, recordRuling, resolveBase, runAudit } from './round.js';\nexport { GUARD_HOOK, mcpServer, mergeClaudeSettings, mergeMcp, mergeSpecGraph, PROJECT_DIR, PROJECT_DIR_OR_HERE } from './configure.js';\nexport type { BriefRow, FileChange, Finding, Severity } from './types.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAG5F,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE9F,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEvF,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzF,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAE7G,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEjG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1H,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["/**\n * Programmatic API. The command line is a thin shell over these; a harness of\n * another kind - a CI job, an orchestrator - can ask the same questions.\n */\n\nexport { main, run, EXIT_ERROR, EXIT_FAILED, EXIT_OK, UsageError, version } from './cli.js';\nexport type { CliIO } from './cli.js';\n\nexport { audit, describeMeasured } from './audit.js';\nexport type { AssertionOutcome, AuditInput, AuditReport, Measured, UnreadableAssertion } from './audit.js';\nexport { briefIdFromBranch, idFromBranch, sameId, templateError } from './branch.js';\nexport { findActive, parseBriefList, SiblingOutputError, SUPPORTED_SCHEMA_VERSIONS } from './briefs.js';\nexport type { ActiveBrief, ActiveSource } from './briefs.js';\nexport { ConfigError, CONFIG_FILE, DEFAULT_CONFIG, parseConfig, SIBLINGS } from './config.js';\nexport type { HarnessConfig, OutOfScope, SiblingName } from './config.js';\nexport { renderContext } from './context.js';\nexport type { CitedDocument, ContextInput, ContextPacket, RuleInForce, RulingInForce, UnreadablePattern, UnreadableRulingPath } from './context.js';\nexport { formatFindings, formatGithub, formatGitlab, formatSarif } from './formats.js';\nexport type { FindingFormat, FormatOptions } from './formats.js';\nexport { decide } from './guard.js';\nexport type { Decision, GuardInput, Reason, Verdict, VerifiedRuling } from './guard.js';\nexport { claudeResponse, gitResponse, parseClaudeHook, WRITING_TOOLS } from './hooks.js';\nexport type { HookRequest, HookResponse } from './hooks.js';\nexport { readJUnit } from './junit.js';\nexport type { JUnitCase, JUnitRead } from './junit.js';\nexport { diffManifest, ecosystemOf, manifestMatcher, readManifest, readManifestNames } from './manifests.js';\nexport type { Dependency, DependencyChange, Ecosystem, ManifestNames } from './manifests.js';\nexport { classify, readProbes, renderEvidence, verdictOf } from './probe.js';\nexport type { Classified, CodeBlock, ProbeFile, ProbeResult, ProbeRun, ProbeSet, ProbeSpec } from './probe.js';\nexport { createReader } from './reader.js';\nexport { addRulingRow, nextId, pathsOf, readRulings, renderMemo, renderRow } from './rulings.js';\nexport type { EscalationOption, EscalationRequest, RulingRow, TableView } from './rulings.js';\nexport { buildContext, checkPaths, checkRulings, raiseEscalation, recordRuling, resolveBase, runAudit } from './round.js';\nexport { GUARD_HOOK, mcpServer, mergeClaudeSettings, mergeMcp, mergeSpecGraph, PROJECT_DIR, PROJECT_DIR_OR_HERE } from './configure.js';\nexport type { BriefRow, FileChange, Finding, Severity } from './types.js';\n"]}
@@ -43,11 +43,18 @@ export interface ManifestNames {
43
43
  readonly name: string;
44
44
  readonly reason: string;
45
45
  }[];
46
+ /** The names a leading `/` roots, alone or on a brace alternative, and whether every alternative is. */
47
+ readonly rooted: readonly {
48
+ readonly name: string;
49
+ readonly whole: boolean;
50
+ }[];
46
51
  }
47
52
  /**
48
53
  * The configured manifest names, read. A name that cannot be read names no
49
54
  * manifest, and is returned so the audit can say so: every file it was
50
- * meant to name would otherwise go unmeasured in silence.
55
+ * meant to name would otherwise go unmeasured in silence. So is a name a
56
+ * leading `/` roots at the filesystem's root: it can be read, and names no
57
+ * path the audit is given, all of which are repository-relative.
51
58
  */
52
59
  export declare function readManifestNames(patterns: readonly string[]): ManifestNames;
53
60
  /** A predicate over paths for the configured manifest names, matched at any depth. */
package/dist/manifests.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * network. A manifest it cannot read is reported as unread, never as clean,
10
10
  * and so is a configured name it cannot read.
11
11
  */
12
- import { parseGlob } from './vendor/spec-core/pattern/index.js';
12
+ import { globAlternatives, parseGlob } from './vendor/spec-core/pattern/index.js';
13
13
  /** The ecosystem a manifest file name belongs to, or `null`. */
14
14
  export function ecosystemOf(path) {
15
15
  const name = path.slice(path.lastIndexOf('/') + 1);
@@ -32,19 +32,29 @@ export function ecosystemOf(path) {
32
32
  /**
33
33
  * The configured manifest names, read. A name that cannot be read names no
34
34
  * manifest, and is returned so the audit can say so: every file it was
35
- * meant to name would otherwise go unmeasured in silence.
35
+ * meant to name would otherwise go unmeasured in silence. So is a name a
36
+ * leading `/` roots at the filesystem's root: it can be read, and names no
37
+ * path the audit is given, all of which are repository-relative.
36
38
  */
37
39
  export function readManifestNames(patterns) {
38
40
  const globs = [];
39
41
  const unread = [];
42
+ const rooted = [];
40
43
  for (const pattern of patterns) {
41
44
  const parsed = parseGlob(pattern, { dialect: 'ripgrep', caseSensitive: true });
42
- if (parsed.ok)
43
- globs.push(parsed.glob);
44
- else
45
+ if (!parsed.ok) {
45
46
  unread.push({ name: pattern, reason: parsed.error });
47
+ continue;
48
+ }
49
+ globs.push(parsed.glob);
50
+ // spec-core refuses here only what parseGlob refuses, so a name read
51
+ // above always has its alternatives read.
52
+ const { alternatives } = globAlternatives(pattern);
53
+ const roots = alternatives.filter((alternative) => alternative.rooted).length;
54
+ if (roots > 0)
55
+ rooted.push({ name: pattern, whole: roots === alternatives.length });
46
56
  }
47
- return { match: (path) => globs.some((glob) => glob.match(path)), unread };
57
+ return { match: (path) => globs.some((glob) => glob.match(path)), unread, rooted };
48
58
  }
49
59
  /** A predicate over paths for the configured manifest names, matched at any depth. */
50
60
  export function manifestMatcher(patterns) {
@@ -1 +1 @@
1
- {"version":3,"file":"manifests.js","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which dependencies a round added, removed or moved, read from the manifests\n * it changed.\n *\n * \"No new dependency without saying so\" is one of the few audit questions\n * that can be answered exactly, and it matters: a dependency is code nobody in\n * the review read. Each reader here understands the part of its format that\n * declares dependencies and nothing more - no resolver, no lockfile, no\n * network. A manifest it cannot read is reported as unread, never as clean,\n * and so is a configured name it cannot read.\n */\n\nimport { parseGlob, type Glob } from './vendor/spec-core/pattern/index.js';\n\nexport type Ecosystem = 'npm' | 'cargo' | 'go' | 'pip' | 'python' | 'nuget' | 'bundler';\n\nexport interface Dependency {\n readonly section: string;\n readonly name: string;\n /** The version or requirement as written; `''` when none is. */\n readonly version: string;\n}\n\nexport interface DependencyChange {\n readonly file: string;\n readonly ecosystem: Ecosystem;\n readonly section: string;\n readonly name: string;\n /** `null` when the dependency is new. */\n readonly before: string | null;\n /** `null` when the dependency was removed. */\n readonly after: string | null;\n}\n\nexport type ManifestRead = { readonly ok: true; readonly dependencies: readonly Dependency[] } | { readonly ok: false; readonly error: string };\n\n/** The ecosystem a manifest file name belongs to, or `null`. */\nexport function ecosystemOf(path: string): Ecosystem | null {\n const name = path.slice(path.lastIndexOf('/') + 1);\n if (name === 'package.json') return 'npm';\n if (name === 'Cargo.toml') return 'cargo';\n if (name === 'go.mod') return 'go';\n if (name === 'pyproject.toml') return 'python';\n if (/^requirements.*\\.txt$/.test(name)) return 'pip';\n if (name === 'Directory.Packages.props' || /\\.(?:cs|fs|vb)proj$/.test(name)) return 'nuget';\n if (name === 'Gemfile') return 'bundler';\n return null;\n}\n\nexport interface ManifestNames {\n /** Whether a path is a manifest by one of the names that could be read, matched at any depth. */\n readonly match: (path: string) => boolean;\n /** The names spec-core's glob refused, malformed or too large to compile, each with its reason. */\n readonly unread: readonly { readonly name: string; readonly reason: string }[];\n}\n\n/**\n * The configured manifest names, read. A name that cannot be read names no\n * manifest, and is returned so the audit can say so: every file it was\n * meant to name would otherwise go unmeasured in silence.\n */\nexport function readManifestNames(patterns: readonly string[]): ManifestNames {\n const globs: Glob[] = [];\n const unread: { name: string; reason: string }[] = [];\n for (const pattern of patterns) {\n const parsed = parseGlob(pattern, { dialect: 'ripgrep', caseSensitive: true });\n if (parsed.ok) globs.push(parsed.glob);\n else unread.push({ name: pattern, reason: parsed.error });\n }\n return { match: (path) => globs.some((glob) => glob.match(path)), unread };\n}\n\n/** A predicate over paths for the configured manifest names, matched at any depth. */\nexport function manifestMatcher(patterns: readonly string[]): (path: string) => boolean {\n return readManifestNames(patterns).match;\n}\n\n/* --------------------------------------------------------------------- npm */\n\nconst NPM_SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];\n\nfunction readNpm(text: string): ManifestRead {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return { ok: false, error: 'not valid JSON' };\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return { ok: false, error: 'not a JSON object' };\n const out: Dependency[] = [];\n for (const section of NPM_SECTIONS) {\n const table = (value as Record<string, unknown>)[section];\n if (table === undefined) continue;\n if (typeof table !== 'object' || table === null || Array.isArray(table)) return { ok: false, error: `\"${section}\" is not an object` };\n for (const [name, version] of Object.entries(table)) out.push({ section, name, version: String(version) });\n }\n return { ok: true, dependencies: out };\n}\n\n/* -------------------------------------------------------------------- TOML */\n\n/** A TOML value on one line, or the start of a multi-line array. Comments and quotes read the way TOML reads them. */\nfunction stripTomlComment(line: string): string {\n let quote: string | null = null;\n for (let i = 0; i < line.length; i += 1) {\n const ch = line.charAt(i);\n if (quote !== null) {\n if (ch === '\\\\' && quote === '\"') i += 1;\n else if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") {\n quote = ch;\n } else if (ch === '#') {\n return line.slice(0, i);\n }\n }\n return line;\n}\n\nfunction unquote(value: string): string {\n const trimmed = value.trim();\n if (trimmed.length >= 2 && (trimmed.startsWith('\"') || trimmed.startsWith(\"'\")) && trimmed.endsWith(trimmed.charAt(0))) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\n/**\n * The strings of a TOML array, `[\"a>=1\", 'b']`, possibly spread over lines.\n * A string inside an inline table is a value of that table, not an element:\n * `{include-group = \"test\"}` in a dependency group names a group, not a package.\n */\nfunction tomlStrings(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let i = 0;\n while (i < text.length) {\n const ch = text.charAt(i);\n if (ch === '{') depth += 1;\n else if (ch === '}') depth -= 1;\n if (ch === '\"' || ch === \"'\") {\n let j = i + 1;\n let value = '';\n while (j < text.length && text.charAt(j) !== ch) {\n if (text.charAt(j) === '\\\\' && ch === '\"') {\n value += text.charAt(j + 1);\n j += 2;\n continue;\n }\n value += text.charAt(j);\n j += 1;\n }\n if (depth === 0) out.push(value);\n i = j + 1;\n continue;\n }\n i += 1;\n }\n return out;\n}\n\n/** The name a PEP 508 requirement names, normalised as PEP 503 says. */\nexport function pythonName(requirement: string): string {\n const match = /^\\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(requirement);\n return (match?.[1] ?? requirement.trim()).toLowerCase().replace(/[-_.]+/g, '-');\n}\n\ninterface TomlLine {\n readonly table: string;\n readonly key: string;\n readonly value: string;\n}\n\n/** Top-level `key = value` lines per table header, arrays gathered across lines. Enough for dependency tables. */\nfunction tomlEntries(text: string): TomlLine[] | string {\n const out: TomlLine[] = [];\n let table = '';\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i += 1) {\n const line = stripTomlComment(lines[i] as string).trim();\n if (line === '') continue;\n const header = /^\\[\\[?\\s*([^\\]]+?)\\s*\\]\\]?$/.exec(line);\n if (header !== null) {\n table = (header[1] as string).replace(/\\s*\\.\\s*/g, '.');\n continue;\n }\n const eq = line.indexOf('=');\n if (eq <= 0) continue;\n const key = unquote(line.slice(0, eq));\n let value = line.slice(eq + 1).trim();\n if (value.startsWith('[') && !balanced(value)) {\n while (i + 1 < lines.length && !balanced(value)) {\n i += 1;\n value += ` ${stripTomlComment(lines[i] as string).trim()}`;\n }\n if (!balanced(value)) return `the array for \"${key}\" is never closed`;\n }\n out.push({ table, key, value });\n }\n return out;\n}\n\nfunction balanced(value: string): boolean {\n let depth = 0;\n let quote: string | null = null;\n for (let i = 0; i < value.length; i += 1) {\n const ch = value.charAt(i);\n if (quote !== null) {\n if (ch === '\\\\' && quote === '\"') i += 1;\n else if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") quote = ch;\n else if (ch === '[') depth += 1;\n else if (ch === ']') depth -= 1;\n }\n return depth <= 0 && quote === null;\n}\n\nconst CARGO_TABLE = /^(?:target\\..+\\.)?((?:workspace\\.)?(?:dev-|build-)?dependencies)(?:\\.(.+))?$/;\n\nfunction readCargo(text: string): ManifestRead {\n const entries = tomlEntries(text);\n if (typeof entries === 'string') return { ok: false, error: entries };\n const out = new Map<string, Dependency>();\n for (const { table, key, value } of entries) {\n const match = CARGO_TABLE.exec(table);\n if (match === null) continue;\n const section = match[1] as string;\n const inline = match[2];\n if (inline !== undefined) {\n // [dependencies.serde] then `version = \"1\"`: the table names the crate.\n const name = unquote(inline);\n const id = `${section}\\u0000${name}`;\n const current = out.get(id) ?? { section, name, version: '' };\n out.set(id, key === 'version' ? { ...current, version: unquote(value) } : current);\n continue;\n }\n const version = value.startsWith('{') ? (/version\\s*=\\s*(\"[^\"]*\"|'[^']*')/.exec(value)?.[1] ?? '') : value;\n out.set(`${section}\\u0000${key}`, { section, name: key, version: unquote(version) });\n }\n return { ok: true, dependencies: [...out.values()] };\n}\n\nfunction readPyproject(text: string): ManifestRead {\n const entries = tomlEntries(text);\n if (typeof entries === 'string') return { ok: false, error: entries };\n const out: Dependency[] = [];\n for (const { table, key, value } of entries) {\n if (table === 'project' && key === 'dependencies') {\n for (const requirement of tomlStrings(value)) out.push({ section: 'project', name: pythonName(requirement), version: requirement });\n } else if (table === 'project.optional-dependencies' || table === 'dependency-groups') {\n for (const requirement of tomlStrings(value)) out.push({ section: `${table}.${key}`, name: pythonName(requirement), version: requirement });\n } else if (/^tool\\.poetry\\.(?:group\\.[^.]+\\.)?(?:dev-)?dependencies$/.test(table) && key.toLowerCase() !== 'python') {\n out.push({ section: table, name: pythonName(key), version: unquote(value) });\n }\n }\n return { ok: true, dependencies: out };\n}\n\n/* ------------------------------------------------------------ line formats */\n\nfunction readGoMod(text: string): ManifestRead {\n const out: Dependency[] = [];\n let block = false;\n for (const raw of text.split(/\\r?\\n/)) {\n const indirect = /\\/\\/\\s*indirect\\b/.test(raw);\n const line = raw.replace(/\\/\\/.*$/, '').trim();\n if (line === '') continue;\n if (block) {\n if (line === ')') {\n block = false;\n continue;\n }\n const [name, version] = line.split(/\\s+/);\n if (name !== undefined) out.push({ section: indirect ? 'require (indirect)' : 'require', name, version: version ?? '' });\n continue;\n }\n if (/^require\\s*\\($/.test(line)) {\n block = true;\n continue;\n }\n const single = /^require\\s+(\\S+)\\s+(\\S+)/.exec(line);\n if (single !== null) out.push({ section: indirect ? 'require (indirect)' : 'require', name: single[1] as string, version: single[2] as string });\n }\n if (block) return { ok: false, error: 'a require block is never closed' };\n return { ok: true, dependencies: out };\n}\n\nfunction readRequirements(text: string): ManifestRead {\n const out: Dependency[] = [];\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.replace(/(^|\\s)#.*$/, '').trim();\n if (line === '' || line.startsWith('-')) continue;\n out.push({ section: 'requirements', name: pythonName(line), version: line });\n }\n return { ok: true, dependencies: out };\n}\n\nfunction readNuget(text: string): ManifestRead {\n const out: Dependency[] = [];\n const element = /<(PackageReference|PackageVersion)\\b([^>]*?)\\/?>/g;\n for (let match = element.exec(text); match !== null; match = element.exec(text)) {\n const attributes = match[2] as string;\n const name = /\\bInclude\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1] ?? /\\bUpdate\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1];\n if (name === undefined) continue;\n const version = /\\bVersion\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1] ?? '';\n out.push({ section: match[1] as string, name, version });\n }\n return { ok: true, dependencies: out };\n}\n\nfunction readGemfile(text: string): ManifestRead {\n const out: Dependency[] = [];\n for (const raw of text.split(/\\r?\\n/)) {\n const match = /^\\s*gem\\s+([\"'])([^\"']+)\\1\\s*(?:,\\s*([\"'])([^\"']*)\\3)?/.exec(raw);\n if (match !== null) out.push({ section: 'gem', name: match[2] as string, version: match[4] ?? '' });\n }\n return { ok: true, dependencies: out };\n}\n\n/** Reads the dependencies a manifest declares. */\nexport function readManifest(ecosystem: Ecosystem, text: string): ManifestRead {\n switch (ecosystem) {\n case 'npm':\n return readNpm(text);\n case 'cargo':\n return readCargo(text);\n case 'go':\n return readGoMod(text);\n case 'python':\n return readPyproject(text);\n case 'pip':\n return readRequirements(text);\n case 'nuget':\n return readNuget(text);\n case 'bundler':\n return readGemfile(text);\n }\n}\n\n/**\n * What changed between two versions of one manifest. `null` text is a file\n * that does not exist on that side. Returns the error instead when either\n * side cannot be read.\n */\nexport function diffManifest(\n file: string,\n ecosystem: Ecosystem,\n before: string | null,\n after: string | null,\n): { readonly changes: readonly DependencyChange[] } | { readonly error: string } {\n const read = (text: string | null, side: string): readonly Dependency[] | string => {\n if (text === null) return [];\n const result = readManifest(ecosystem, text);\n return result.ok ? result.dependencies : `${file} (${side}): ${result.error}`;\n };\n const old = read(before, 'before');\n if (typeof old === 'string') return { error: old };\n const now = read(after, 'after');\n if (typeof now === 'string') return { error: now };\n const key = (d: Dependency): string => `${d.section}\\u0000${d.name}`;\n const was = new Map(old.map((d) => [key(d), d]));\n const is = new Map(now.map((d) => [key(d), d]));\n const changes: DependencyChange[] = [];\n for (const [id, dependency] of is) {\n const previous = was.get(id);\n if (previous === undefined || previous.version !== dependency.version) {\n changes.push({ file, ecosystem, section: dependency.section, name: dependency.name, before: previous?.version ?? null, after: dependency.version });\n }\n }\n for (const [id, dependency] of was) {\n if (!is.has(id)) changes.push({ file, ecosystem, section: dependency.section, name: dependency.name, before: dependency.version, after: null });\n }\n return { changes };\n}\n"]}
1
+ {"version":3,"file":"manifests.js","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAmC,MAAM,qCAAqC,CAAC;AAwBnH,gEAAgE;AAChE,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,IAAI,IAAI,KAAK,cAAc;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,OAAO,CAAC;IAC1C,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,IAAI,KAAK,gBAAgB;QAAE,OAAO,QAAQ,CAAC;IAC/C,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACrD,IAAI,IAAI,KAAK,0BAA0B,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IAC5F,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,IAAI,CAAC;AACd,CAAC;AAWD;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAA2B;IAC3D,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,MAAM,GAAuC,EAAE,CAAC;IACtD,MAAM,MAAM,GAAuC,EAAE,CAAC;IACtD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;YACrD,SAAS;QACX,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,qEAAqE;QACrE,0CAA0C;QAC1C,MAAM,EAAE,YAAY,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAA0D,CAAC;QAC5G,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;QAC9E,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACrF,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAAC,QAA2B;IACzD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;AAC3C,CAAC;AAED,+EAA+E;AAE/E,MAAM,YAAY,GAAG,CAAC,cAAc,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,kBAAkB,CAAC,CAAC;AAErG,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAChD,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC1H,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACnC,MAAM,KAAK,GAAI,KAAiC,CAAC,OAAO,CAAC,CAAC;QAC1D,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,OAAO,oBAAoB,EAAE,CAAC;QACtI,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;AACzC,CAAC;AAED,+EAA+E;AAE/E,sHAAsH;AACtH,SAAS,gBAAgB,CAAC,IAAY;IACpC,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,EAAE,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG;gBAAE,CAAC,IAAI,CAAC,CAAC;iBACpC,IAAI,EAAE,KAAK,KAAK;gBAAE,KAAK,GAAG,IAAI,CAAC;QACtC,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACpC,KAAK,GAAG,EAAE,CAAC;QACb,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvH,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAC;aACtB,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAC;QAChC,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,IAAI,KAAK,GAAG,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;gBAChD,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;oBAC1C,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC5B,CAAC,IAAI,CAAC,CAAC;oBACP,SAAS;gBACX,CAAC;gBACD,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACxB,CAAC,IAAI,CAAC,CAAC;YACT,CAAC;YACD,IAAI,KAAK,KAAK,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACV,SAAS;QACX,CAAC;QACD,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,UAAU,CAAC,WAAmB;IAC5C,MAAM,KAAK,GAAG,kCAAkC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAClF,CAAC;AAQD,kHAAkH;AAClH,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,GAAG,GAAe,EAAE,CAAC;IAC3B,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAC,IAAI,EAAE,CAAC;QACzD,IAAI,IAAI,KAAK,EAAE;YAAE,SAAS;QAC1B,MAAM,MAAM,GAAG,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,KAAK,GAAI,MAAM,CAAC,CAAC,CAAY,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YACxD,SAAS;QACX,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,IAAI,CAAC;YAAE,SAAS;QACtB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACvC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChD,CAAC,IAAI,CAAC,CAAC;gBACP,KAAK,IAAI,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7D,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,OAAO,kBAAkB,GAAG,mBAAmB,CAAC;QACxE,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa;IAC7B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,EAAE,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG;gBAAE,CAAC,IAAI,CAAC,CAAC;iBACpC,IAAI,EAAE,KAAK,KAAK;gBAAE,KAAK,GAAG,IAAI,CAAC;QACtC,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,GAAG,EAAE,CAAC;aAC3C,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAC;aAC3B,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC;AACtC,CAAC;AAED,MAAM,WAAW,GAAG,8EAA8E,CAAC;AAEnG,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACtE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC1C,KAAK,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAW,CAAC;QACnC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,wEAAwE;YACxE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAC7B,MAAM,EAAE,GAAG,GAAG,OAAO,SAAS,IAAI,EAAE,CAAC;YACrC,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;YAC9D,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACnF,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,iCAAiC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC3G,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,SAAS,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;AACvD,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACtE,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS,IAAI,GAAG,KAAK,cAAc,EAAE,CAAC;YAClD,KAAK,MAAM,WAAW,IAAI,WAAW,CAAC,KAAK,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QACtI,CAAC;aAAM,IAAI,KAAK,KAAK,+BAA+B,IAAI,KAAK,KAAK,mBAAmB,EAAE,CAAC;YACtF,KAAK,MAAM,WAAW,IAAI,WAAW,CAAC,KAAK,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QAC9I,CAAC;aAAM,IAAI,0DAA0D,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,QAAQ,EAAE,CAAC;YACpH,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;AACzC,CAAC;AAED,+EAA+E;AAE/E,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,IAAI,IAAI,KAAK,EAAE;YAAE,SAAS;QAC1B,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACjB,KAAK,GAAG,KAAK,CAAC;gBACd,SAAS;YACX,CAAC;YACD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1C,IAAI,IAAI,KAAK,SAAS;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC;YACzH,SAAS;QACX,CAAC;QACD,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,KAAK,GAAG,IAAI,CAAC;YACb,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,MAAM,KAAK,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAW,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAW,EAAE,CAAC,CAAC;IACnJ,CAAC;IACD,IAAI,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;IAC1E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAClD,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,mDAAmD,CAAC;IACpE,KAAK,IAAI,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChF,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAW,CAAC;QACtC,MAAM,IAAI,GAAG,2BAA2B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnH,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS;QACjC,MAAM,OAAO,GAAG,2BAA2B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAW,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,wDAAwD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjF,IAAI,KAAK,KAAK,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACtG,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;AACzC,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,YAAY,CAAC,SAAoB,EAAE,IAAY;IAC7D,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,KAAK;YACR,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,KAAK,OAAO;YACV,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,IAAI;YACP,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;QAC7B,KAAK,KAAK;YACR,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAChC,KAAK,OAAO;YACV,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,SAAoB,EACpB,MAAqB,EACrB,KAAoB;IAEpB,MAAM,IAAI,GAAG,CAAC,IAAmB,EAAE,IAAY,EAAkC,EAAE;QACjF,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAC7C,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAChF,CAAC,CAAC;IACF,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACnD,MAAM,GAAG,GAAG,CAAC,CAAa,EAAU,EAAE,CAAC,GAAG,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;YACtE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,IAAI,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;QACtJ,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,GAAG,EAAE,CAAC;QACnC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClJ,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC","sourcesContent":["/**\n * Which dependencies a round added, removed or moved, read from the manifests\n * it changed.\n *\n * \"No new dependency without saying so\" is one of the few audit questions\n * that can be answered exactly, and it matters: a dependency is code nobody in\n * the review read. Each reader here understands the part of its format that\n * declares dependencies and nothing more - no resolver, no lockfile, no\n * network. A manifest it cannot read is reported as unread, never as clean,\n * and so is a configured name it cannot read.\n */\n\nimport { globAlternatives, parseGlob, type Glob, type GlobAlternative } from './vendor/spec-core/pattern/index.js';\n\nexport type Ecosystem = 'npm' | 'cargo' | 'go' | 'pip' | 'python' | 'nuget' | 'bundler';\n\nexport interface Dependency {\n readonly section: string;\n readonly name: string;\n /** The version or requirement as written; `''` when none is. */\n readonly version: string;\n}\n\nexport interface DependencyChange {\n readonly file: string;\n readonly ecosystem: Ecosystem;\n readonly section: string;\n readonly name: string;\n /** `null` when the dependency is new. */\n readonly before: string | null;\n /** `null` when the dependency was removed. */\n readonly after: string | null;\n}\n\nexport type ManifestRead = { readonly ok: true; readonly dependencies: readonly Dependency[] } | { readonly ok: false; readonly error: string };\n\n/** The ecosystem a manifest file name belongs to, or `null`. */\nexport function ecosystemOf(path: string): Ecosystem | null {\n const name = path.slice(path.lastIndexOf('/') + 1);\n if (name === 'package.json') return 'npm';\n if (name === 'Cargo.toml') return 'cargo';\n if (name === 'go.mod') return 'go';\n if (name === 'pyproject.toml') return 'python';\n if (/^requirements.*\\.txt$/.test(name)) return 'pip';\n if (name === 'Directory.Packages.props' || /\\.(?:cs|fs|vb)proj$/.test(name)) return 'nuget';\n if (name === 'Gemfile') return 'bundler';\n return null;\n}\n\nexport interface ManifestNames {\n /** Whether a path is a manifest by one of the names that could be read, matched at any depth. */\n readonly match: (path: string) => boolean;\n /** The names spec-core's glob refused, malformed or too large to compile, each with its reason. */\n readonly unread: readonly { readonly name: string; readonly reason: string }[];\n /** The names a leading `/` roots, alone or on a brace alternative, and whether every alternative is. */\n readonly rooted: readonly { readonly name: string; readonly whole: boolean }[];\n}\n\n/**\n * The configured manifest names, read. A name that cannot be read names no\n * manifest, and is returned so the audit can say so: every file it was\n * meant to name would otherwise go unmeasured in silence. So is a name a\n * leading `/` roots at the filesystem's root: it can be read, and names no\n * path the audit is given, all of which are repository-relative.\n */\nexport function readManifestNames(patterns: readonly string[]): ManifestNames {\n const globs: Glob[] = [];\n const unread: { name: string; reason: string }[] = [];\n const rooted: { name: string; whole: boolean }[] = [];\n for (const pattern of patterns) {\n const parsed = parseGlob(pattern, { dialect: 'ripgrep', caseSensitive: true });\n if (!parsed.ok) {\n unread.push({ name: pattern, reason: parsed.error });\n continue;\n }\n globs.push(parsed.glob);\n // spec-core refuses here only what parseGlob refuses, so a name read\n // above always has its alternatives read.\n const { alternatives } = globAlternatives(pattern) as { readonly alternatives: readonly GlobAlternative[] };\n const roots = alternatives.filter((alternative) => alternative.rooted).length;\n if (roots > 0) rooted.push({ name: pattern, whole: roots === alternatives.length });\n }\n return { match: (path) => globs.some((glob) => glob.match(path)), unread, rooted };\n}\n\n/** A predicate over paths for the configured manifest names, matched at any depth. */\nexport function manifestMatcher(patterns: readonly string[]): (path: string) => boolean {\n return readManifestNames(patterns).match;\n}\n\n/* --------------------------------------------------------------------- npm */\n\nconst NPM_SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];\n\nfunction readNpm(text: string): ManifestRead {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return { ok: false, error: 'not valid JSON' };\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return { ok: false, error: 'not a JSON object' };\n const out: Dependency[] = [];\n for (const section of NPM_SECTIONS) {\n const table = (value as Record<string, unknown>)[section];\n if (table === undefined) continue;\n if (typeof table !== 'object' || table === null || Array.isArray(table)) return { ok: false, error: `\"${section}\" is not an object` };\n for (const [name, version] of Object.entries(table)) out.push({ section, name, version: String(version) });\n }\n return { ok: true, dependencies: out };\n}\n\n/* -------------------------------------------------------------------- TOML */\n\n/** A TOML value on one line, or the start of a multi-line array. Comments and quotes read the way TOML reads them. */\nfunction stripTomlComment(line: string): string {\n let quote: string | null = null;\n for (let i = 0; i < line.length; i += 1) {\n const ch = line.charAt(i);\n if (quote !== null) {\n if (ch === '\\\\' && quote === '\"') i += 1;\n else if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") {\n quote = ch;\n } else if (ch === '#') {\n return line.slice(0, i);\n }\n }\n return line;\n}\n\nfunction unquote(value: string): string {\n const trimmed = value.trim();\n if (trimmed.length >= 2 && (trimmed.startsWith('\"') || trimmed.startsWith(\"'\")) && trimmed.endsWith(trimmed.charAt(0))) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\n/**\n * The strings of a TOML array, `[\"a>=1\", 'b']`, possibly spread over lines.\n * A string inside an inline table is a value of that table, not an element:\n * `{include-group = \"test\"}` in a dependency group names a group, not a package.\n */\nfunction tomlStrings(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let i = 0;\n while (i < text.length) {\n const ch = text.charAt(i);\n if (ch === '{') depth += 1;\n else if (ch === '}') depth -= 1;\n if (ch === '\"' || ch === \"'\") {\n let j = i + 1;\n let value = '';\n while (j < text.length && text.charAt(j) !== ch) {\n if (text.charAt(j) === '\\\\' && ch === '\"') {\n value += text.charAt(j + 1);\n j += 2;\n continue;\n }\n value += text.charAt(j);\n j += 1;\n }\n if (depth === 0) out.push(value);\n i = j + 1;\n continue;\n }\n i += 1;\n }\n return out;\n}\n\n/** The name a PEP 508 requirement names, normalised as PEP 503 says. */\nexport function pythonName(requirement: string): string {\n const match = /^\\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(requirement);\n return (match?.[1] ?? requirement.trim()).toLowerCase().replace(/[-_.]+/g, '-');\n}\n\ninterface TomlLine {\n readonly table: string;\n readonly key: string;\n readonly value: string;\n}\n\n/** Top-level `key = value` lines per table header, arrays gathered across lines. Enough for dependency tables. */\nfunction tomlEntries(text: string): TomlLine[] | string {\n const out: TomlLine[] = [];\n let table = '';\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i += 1) {\n const line = stripTomlComment(lines[i] as string).trim();\n if (line === '') continue;\n const header = /^\\[\\[?\\s*([^\\]]+?)\\s*\\]\\]?$/.exec(line);\n if (header !== null) {\n table = (header[1] as string).replace(/\\s*\\.\\s*/g, '.');\n continue;\n }\n const eq = line.indexOf('=');\n if (eq <= 0) continue;\n const key = unquote(line.slice(0, eq));\n let value = line.slice(eq + 1).trim();\n if (value.startsWith('[') && !balanced(value)) {\n while (i + 1 < lines.length && !balanced(value)) {\n i += 1;\n value += ` ${stripTomlComment(lines[i] as string).trim()}`;\n }\n if (!balanced(value)) return `the array for \"${key}\" is never closed`;\n }\n out.push({ table, key, value });\n }\n return out;\n}\n\nfunction balanced(value: string): boolean {\n let depth = 0;\n let quote: string | null = null;\n for (let i = 0; i < value.length; i += 1) {\n const ch = value.charAt(i);\n if (quote !== null) {\n if (ch === '\\\\' && quote === '\"') i += 1;\n else if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") quote = ch;\n else if (ch === '[') depth += 1;\n else if (ch === ']') depth -= 1;\n }\n return depth <= 0 && quote === null;\n}\n\nconst CARGO_TABLE = /^(?:target\\..+\\.)?((?:workspace\\.)?(?:dev-|build-)?dependencies)(?:\\.(.+))?$/;\n\nfunction readCargo(text: string): ManifestRead {\n const entries = tomlEntries(text);\n if (typeof entries === 'string') return { ok: false, error: entries };\n const out = new Map<string, Dependency>();\n for (const { table, key, value } of entries) {\n const match = CARGO_TABLE.exec(table);\n if (match === null) continue;\n const section = match[1] as string;\n const inline = match[2];\n if (inline !== undefined) {\n // [dependencies.serde] then `version = \"1\"`: the table names the crate.\n const name = unquote(inline);\n const id = `${section}\\u0000${name}`;\n const current = out.get(id) ?? { section, name, version: '' };\n out.set(id, key === 'version' ? { ...current, version: unquote(value) } : current);\n continue;\n }\n const version = value.startsWith('{') ? (/version\\s*=\\s*(\"[^\"]*\"|'[^']*')/.exec(value)?.[1] ?? '') : value;\n out.set(`${section}\\u0000${key}`, { section, name: key, version: unquote(version) });\n }\n return { ok: true, dependencies: [...out.values()] };\n}\n\nfunction readPyproject(text: string): ManifestRead {\n const entries = tomlEntries(text);\n if (typeof entries === 'string') return { ok: false, error: entries };\n const out: Dependency[] = [];\n for (const { table, key, value } of entries) {\n if (table === 'project' && key === 'dependencies') {\n for (const requirement of tomlStrings(value)) out.push({ section: 'project', name: pythonName(requirement), version: requirement });\n } else if (table === 'project.optional-dependencies' || table === 'dependency-groups') {\n for (const requirement of tomlStrings(value)) out.push({ section: `${table}.${key}`, name: pythonName(requirement), version: requirement });\n } else if (/^tool\\.poetry\\.(?:group\\.[^.]+\\.)?(?:dev-)?dependencies$/.test(table) && key.toLowerCase() !== 'python') {\n out.push({ section: table, name: pythonName(key), version: unquote(value) });\n }\n }\n return { ok: true, dependencies: out };\n}\n\n/* ------------------------------------------------------------ line formats */\n\nfunction readGoMod(text: string): ManifestRead {\n const out: Dependency[] = [];\n let block = false;\n for (const raw of text.split(/\\r?\\n/)) {\n const indirect = /\\/\\/\\s*indirect\\b/.test(raw);\n const line = raw.replace(/\\/\\/.*$/, '').trim();\n if (line === '') continue;\n if (block) {\n if (line === ')') {\n block = false;\n continue;\n }\n const [name, version] = line.split(/\\s+/);\n if (name !== undefined) out.push({ section: indirect ? 'require (indirect)' : 'require', name, version: version ?? '' });\n continue;\n }\n if (/^require\\s*\\($/.test(line)) {\n block = true;\n continue;\n }\n const single = /^require\\s+(\\S+)\\s+(\\S+)/.exec(line);\n if (single !== null) out.push({ section: indirect ? 'require (indirect)' : 'require', name: single[1] as string, version: single[2] as string });\n }\n if (block) return { ok: false, error: 'a require block is never closed' };\n return { ok: true, dependencies: out };\n}\n\nfunction readRequirements(text: string): ManifestRead {\n const out: Dependency[] = [];\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.replace(/(^|\\s)#.*$/, '').trim();\n if (line === '' || line.startsWith('-')) continue;\n out.push({ section: 'requirements', name: pythonName(line), version: line });\n }\n return { ok: true, dependencies: out };\n}\n\nfunction readNuget(text: string): ManifestRead {\n const out: Dependency[] = [];\n const element = /<(PackageReference|PackageVersion)\\b([^>]*?)\\/?>/g;\n for (let match = element.exec(text); match !== null; match = element.exec(text)) {\n const attributes = match[2] as string;\n const name = /\\bInclude\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1] ?? /\\bUpdate\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1];\n if (name === undefined) continue;\n const version = /\\bVersion\\s*=\\s*\"([^\"]*)\"/.exec(attributes)?.[1] ?? '';\n out.push({ section: match[1] as string, name, version });\n }\n return { ok: true, dependencies: out };\n}\n\nfunction readGemfile(text: string): ManifestRead {\n const out: Dependency[] = [];\n for (const raw of text.split(/\\r?\\n/)) {\n const match = /^\\s*gem\\s+([\"'])([^\"']+)\\1\\s*(?:,\\s*([\"'])([^\"']*)\\3)?/.exec(raw);\n if (match !== null) out.push({ section: 'gem', name: match[2] as string, version: match[4] ?? '' });\n }\n return { ok: true, dependencies: out };\n}\n\n/** Reads the dependencies a manifest declares. */\nexport function readManifest(ecosystem: Ecosystem, text: string): ManifestRead {\n switch (ecosystem) {\n case 'npm':\n return readNpm(text);\n case 'cargo':\n return readCargo(text);\n case 'go':\n return readGoMod(text);\n case 'python':\n return readPyproject(text);\n case 'pip':\n return readRequirements(text);\n case 'nuget':\n return readNuget(text);\n case 'bundler':\n return readGemfile(text);\n }\n}\n\n/**\n * What changed between two versions of one manifest. `null` text is a file\n * that does not exist on that side. Returns the error instead when either\n * side cannot be read.\n */\nexport function diffManifest(\n file: string,\n ecosystem: Ecosystem,\n before: string | null,\n after: string | null,\n): { readonly changes: readonly DependencyChange[] } | { readonly error: string } {\n const read = (text: string | null, side: string): readonly Dependency[] | string => {\n if (text === null) return [];\n const result = readManifest(ecosystem, text);\n return result.ok ? result.dependencies : `${file} (${side}): ${result.error}`;\n };\n const old = read(before, 'before');\n if (typeof old === 'string') return { error: old };\n const now = read(after, 'after');\n if (typeof now === 'string') return { error: now };\n const key = (d: Dependency): string => `${d.section}\\u0000${d.name}`;\n const was = new Map(old.map((d) => [key(d), d]));\n const is = new Map(now.map((d) => [key(d), d]));\n const changes: DependencyChange[] = [];\n for (const [id, dependency] of is) {\n const previous = was.get(id);\n if (previous === undefined || previous.version !== dependency.version) {\n changes.push({ file, ecosystem, section: dependency.section, name: dependency.name, before: previous?.version ?? null, after: dependency.version });\n }\n }\n for (const [id, dependency] of was) {\n if (!is.has(id)) changes.push({ file, ecosystem, section: dependency.section, name: dependency.name, before: dependency.version, after: null });\n }\n return { changes };\n}\n"]}
@@ -15,6 +15,9 @@
15
15
  * one the flag, SPEC_BRIEF or the branch names - is reported as the audit
16
16
  * reports it, retired as the round intends, and fails nothing: on the round's
17
17
  * branch, a premise that no longer holds is the work being done.
18
+ *
19
+ * A premise spec-guard cannot read is checked by nothing, and is a warning,
20
+ * as it is in the audit: `--strict` fails the run on it.
18
21
  */
19
22
  import { type CliIO, type Options } from './workspace.js';
20
23
  export declare function premisesCommand(options: Options, io: CliIO): Promise<number>;
package/dist/premises.js CHANGED
@@ -15,13 +15,18 @@
15
15
  * one the flag, SPEC_BRIEF or the branch names - is reported as the audit
16
16
  * reports it, retired as the round intends, and fails nothing: on the round's
17
17
  * branch, a premise that no longer holds is the work being done.
18
+ *
19
+ * A premise spec-guard cannot read is checked by nothing, and is a warning,
20
+ * as it is in the audit: `--strict` fails the run on it.
18
21
  */
19
- import { isPremise, premiseFinding } from './audit.js';
22
+ import { isPremise, premiseFinding, unreadablePremiseFinding } from './audit.js';
20
23
  import { briefIdFromBranch } from './branch.js';
21
24
  import { findActive } from './briefs.js';
25
+ import { CONFIG_FILE } from './config.js';
26
+ import { formatFindings } from './formats.js';
22
27
  import { createReader } from './reader.js';
23
28
  import { briefText } from './round.js';
24
- import { EXIT_ERROR, EXIT_FAILED, EXIT_OK, json, openWorkspace } from './workspace.js';
29
+ import { EXIT_ERROR, EXIT_FAILED, EXIT_OK, json, openWorkspace, version } from './workspace.js';
25
30
  export async function premisesCommand(options, io) {
26
31
  const workspace = await openWorkspace(options, io);
27
32
  const reader = createReader();
@@ -31,7 +36,12 @@ export async function premisesCommand(options, io) {
31
36
  const active = findActive(briefs, { flag: options.brief, environment: io.env['SPEC_BRIEF'], branch: fromBranch });
32
37
  const round = active.kind === 'found' ? active.brief.file : null;
33
38
  if (live.length === 0) {
34
- io.stdout.write(options.format === 'json' ? json('premises', { ok: true, checked: 0, findings: [] }) : 'no live brief\n');
39
+ if (options.format === 'json')
40
+ io.stdout.write(json('premises', { ok: true, checked: 0, findings: [] }));
41
+ else if (options.format === 'pretty')
42
+ io.stdout.write('no live brief\n');
43
+ else
44
+ io.stdout.write(formatFindings(options.format, [], { file: CONFIG_FILE, version: version(), summary: 'no live brief' }));
35
45
  return EXIT_OK;
36
46
  }
37
47
  const answer = await workspace.siblings.json('spec-guard', [...live.map((brief) => brief.file), '--ignore-status', '--json']);
@@ -43,35 +53,55 @@ export async function premisesCommand(options, io) {
43
53
  io.stderr.write('spec-harness: spec-guard could not run the briefs\' assertions\n');
44
54
  return EXIT_ERROR;
45
55
  }
46
- const results = (answer.document.results ?? []).filter((result) => result.spec !== undefined);
56
+ const document = answer.document;
57
+ const results = (document.results ?? []).filter((result) => result.spec !== undefined);
58
+ // A directive spec-guard cannot read is listed apart from the results; one
59
+ // in a premise section is a premise nothing checks.
60
+ const errors = (document.errors ?? []).filter((error) => error.spec !== undefined);
61
+ const inBrief = (spec, file) => spec?.file.replace(/\\/g, '/') === file;
47
62
  const findings = [];
48
63
  let premises = 0;
49
64
  for (const brief of live) {
50
- const own = results.filter((result) => result.spec?.file.replace(/\\/g, '/') === brief.file);
51
- if (own.length === 0)
65
+ const own = results.filter((result) => inBrief(result.spec, brief.file));
66
+ const unread = errors.filter((error) => inBrief(error.spec, brief.file));
67
+ if (own.length === 0 && unread.length === 0)
52
68
  continue;
53
69
  const text = await briefText(workspace, brief);
70
+ const premise = (line) => isPremise(reader.sectionsAt(text, line), workspace.config.assertions.premises);
54
71
  for (const result of own) {
55
72
  const line = result.spec?.line ?? 0;
56
- if (!isPremise(reader.sectionsAt(text, line), workspace.config.assertions.premises))
73
+ if (!premise(line))
57
74
  continue;
58
75
  premises += 1;
59
76
  if (result.ok)
60
77
  continue;
61
78
  findings.push(premiseFinding(brief, { description: result.description, message: result.message, line }, brief.file === round));
62
79
  }
80
+ for (const error of unread) {
81
+ const line = error.spec?.line ?? 0;
82
+ if (premise(line))
83
+ findings.push(unreadablePremiseFinding(brief, { message: error.message, line, raw: error.raw ?? '' }));
84
+ }
63
85
  }
64
86
  const stale = findings.filter((finding) => finding.severity === 'error').length;
65
- const retired = findings.length - stale;
87
+ const retired = findings.filter((finding) => finding.severity === 'note').length;
88
+ const unreadable = findings.filter((finding) => finding.severity === 'warning').length;
89
+ const intended = retired === 0 || active.kind !== 'found' ? '' : `, and ${retired} retired by the round on brief ${active.brief.id}, as it intends`;
90
+ const unchecked = unreadable === 0 ? '' : `; ${unreadable} premise(s) spec-guard cannot read, so not checked`;
91
+ const summary = `${premises} premise(s) in ${live.length} live brief(s), ${stale} no longer hold${intended}${unchecked}`;
92
+ // As the audit does: a warning fails the run under --strict.
93
+ const failed = stale > 0 || (options.strict && unreadable > 0);
66
94
  if (options.format === 'json') {
67
- io.stdout.write(json('premises', { ok: stale === 0, briefs: live.length, premises, findings }));
95
+ io.stdout.write(json('premises', { ok: !failed, briefs: live.length, premises, unreadable, findings }));
68
96
  }
69
- else {
97
+ else if (options.format === 'pretty') {
70
98
  for (const finding of findings)
71
99
  io.stdout.write(`${finding.severity.padEnd(8)} ${finding.file}:${finding.line} ${finding.message}\n ${finding.hint}\n`);
72
- const intended = retired === 0 || active.kind !== 'found' ? '' : `, and ${retired} retired by the round on brief ${active.brief.id}, as it intends`;
73
- io.stdout.write(`${premises} premise(s) in ${live.length} live brief(s), ${stale} no longer hold${intended}\n`);
100
+ io.stdout.write(`${summary}\n`);
101
+ }
102
+ else {
103
+ io.stdout.write(formatFindings(options.format, findings, { file: CONFIG_FILE, version: version(), summary }));
74
104
  }
75
- return stale > 0 ? EXIT_FAILED : EXIT_OK;
105
+ return failed ? EXIT_FAILED : EXIT_OK;
76
106
  }
77
107
  //# sourceMappingURL=premises.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"premises.js","sourceRoot":"","sources":["../src/premises.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `spec-harness premises`: is every live brief still about something true?\n *\n * A brief written against a defect states the defect as a premise - an\n * assertion under a section such as \"The Defect, Measured\" that holds while\n * the defect does. When the premise stops holding before the round has run,\n * the brief is stale: someone fixed the defect another way, or the code it\n * describes is gone. A repository that keeps its premises executable can say\n * so on every build, and ask for the brief to be archived or rewritten before\n * an agent is sent to fix what is already fixed.\n *\n * Goals are the audit's (`audit`), not this: a goal fails until its round is\n * done, and a build that failed for that would fail for every brief in flight.\n * For the same reason the premise of the brief a round is working on - the\n * one the flag, SPEC_BRIEF or the branch names - is reported as the audit\n * reports it, retired as the round intends, and fails nothing: on the round's\n * branch, a premise that no longer holds is the work being done.\n */\n\nimport { isPremise, premiseFinding } from './audit.js';\nimport { briefIdFromBranch } from './branch.js';\nimport { findActive } from './briefs.js';\nimport { createReader } from './reader.js';\nimport { briefText } from './round.js';\nimport type { Finding } from './types.js';\nimport { EXIT_ERROR, EXIT_FAILED, EXIT_OK, json, openWorkspace, type CliIO, type Options } from './workspace.js';\n\ninterface GuardResult {\n readonly ok: boolean;\n readonly description: string;\n readonly message: string;\n readonly spec?: { readonly file: string; readonly line: number };\n}\n\nexport async function premisesCommand(options: Options, io: CliIO): Promise<number> {\n const workspace = await openWorkspace(options, io);\n const reader = createReader();\n const briefs = await workspace.siblings.briefs();\n const live = briefs.filter((brief) => brief.phase === 'live');\n const fromBranch = workspace.branch === null ? null : briefIdFromBranch(workspace.config.branches, workspace.branch);\n const active = findActive(briefs, { flag: options.brief, environment: io.env['SPEC_BRIEF'], branch: fromBranch });\n const round = active.kind === 'found' ? active.brief.file : null;\n if (live.length === 0) {\n io.stdout.write(options.format === 'json' ? json('premises', { ok: true, checked: 0, findings: [] }) : 'no live brief\\n');\n return EXIT_OK;\n }\n const answer = await workspace.siblings.json('spec-guard', [...live.map((brief) => brief.file), '--ignore-status', '--json']);\n if ('absent' in answer) {\n io.stderr.write(`spec-harness: premises cannot be checked: ${answer.absent}\\n`);\n return EXIT_ERROR;\n }\n if (answer.code === 2) {\n io.stderr.write('spec-harness: spec-guard could not run the briefs\\' assertions\\n');\n return EXIT_ERROR;\n }\n const results = ((answer.document as { results?: GuardResult[] }).results ?? []).filter((result) => result.spec !== undefined);\n const findings: Finding[] = [];\n let premises = 0;\n for (const brief of live) {\n const own = results.filter((result) => result.spec?.file.replace(/\\\\/g, '/') === brief.file);\n if (own.length === 0) continue;\n const text = await briefText(workspace, brief);\n for (const result of own) {\n const line = result.spec?.line ?? 0;\n if (!isPremise(reader.sectionsAt(text, line), workspace.config.assertions.premises)) continue;\n premises += 1;\n if (result.ok) continue;\n findings.push(premiseFinding(brief, { description: result.description, message: result.message, line }, brief.file === round));\n }\n }\n const stale = findings.filter((finding) => finding.severity === 'error').length;\n const retired = findings.length - stale;\n if (options.format === 'json') {\n io.stdout.write(json('premises', { ok: stale === 0, briefs: live.length, premises, findings }));\n } else {\n for (const finding of findings) io.stdout.write(`${finding.severity.padEnd(8)} ${finding.file}:${finding.line} ${finding.message}\\n ${finding.hint}\\n`);\n const intended = retired === 0 || active.kind !== 'found' ? '' : `, and ${retired} retired by the round on brief ${active.brief.id}, as it intends`;\n io.stdout.write(`${premises} premise(s) in ${live.length} live brief(s), ${stale} no longer hold${intended}\\n`);\n }\n return stale > 0 ? EXIT_FAILED : EXIT_OK;\n}\n"]}
1
+ {"version":3,"file":"premises.js","sourceRoot":"","sources":["../src/premises.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `spec-harness premises`: is every live brief still about something true?\n *\n * A brief written against a defect states the defect as a premise - an\n * assertion under a section such as \"The Defect, Measured\" that holds while\n * the defect does. When the premise stops holding before the round has run,\n * the brief is stale: someone fixed the defect another way, or the code it\n * describes is gone. A repository that keeps its premises executable can say\n * so on every build, and ask for the brief to be archived or rewritten before\n * an agent is sent to fix what is already fixed.\n *\n * Goals are the audit's (`audit`), not this: a goal fails until its round is\n * done, and a build that failed for that would fail for every brief in flight.\n * For the same reason the premise of the brief a round is working on - the\n * one the flag, SPEC_BRIEF or the branch names - is reported as the audit\n * reports it, retired as the round intends, and fails nothing: on the round's\n * branch, a premise that no longer holds is the work being done.\n *\n * A premise spec-guard cannot read is checked by nothing, and is a warning,\n * as it is in the audit: `--strict` fails the run on it.\n */\n\nimport { isPremise, premiseFinding, unreadablePremiseFinding } from './audit.js';\nimport { briefIdFromBranch } from './branch.js';\nimport { findActive } from './briefs.js';\nimport { CONFIG_FILE } from './config.js';\nimport { formatFindings } from './formats.js';\nimport { createReader } from './reader.js';\nimport { briefText } from './round.js';\nimport type { Finding } from './types.js';\nimport { EXIT_ERROR, EXIT_FAILED, EXIT_OK, json, openWorkspace, version, type CliIO, type Options } from './workspace.js';\n\ninterface GuardResult {\n readonly ok: boolean;\n readonly description: string;\n readonly message: string;\n readonly spec?: { readonly file: string; readonly line: number };\n}\n\ninterface GuardError {\n readonly message: string;\n readonly raw?: string;\n readonly spec?: { readonly file: string; readonly line: number };\n}\n\nexport async function premisesCommand(options: Options, io: CliIO): Promise<number> {\n const workspace = await openWorkspace(options, io);\n const reader = createReader();\n const briefs = await workspace.siblings.briefs();\n const live = briefs.filter((brief) => brief.phase === 'live');\n const fromBranch = workspace.branch === null ? null : briefIdFromBranch(workspace.config.branches, workspace.branch);\n const active = findActive(briefs, { flag: options.brief, environment: io.env['SPEC_BRIEF'], branch: fromBranch });\n const round = active.kind === 'found' ? active.brief.file : null;\n if (live.length === 0) {\n if (options.format === 'json') io.stdout.write(json('premises', { ok: true, checked: 0, findings: [] }));\n else if (options.format === 'pretty') io.stdout.write('no live brief\\n');\n else io.stdout.write(formatFindings(options.format, [], { file: CONFIG_FILE, version: version(), summary: 'no live brief' }));\n return EXIT_OK;\n }\n const answer = await workspace.siblings.json('spec-guard', [...live.map((brief) => brief.file), '--ignore-status', '--json']);\n if ('absent' in answer) {\n io.stderr.write(`spec-harness: premises cannot be checked: ${answer.absent}\\n`);\n return EXIT_ERROR;\n }\n if (answer.code === 2) {\n io.stderr.write('spec-harness: spec-guard could not run the briefs\\' assertions\\n');\n return EXIT_ERROR;\n }\n const document = answer.document as { results?: GuardResult[]; errors?: GuardError[] };\n const results = (document.results ?? []).filter((result) => result.spec !== undefined);\n // A directive spec-guard cannot read is listed apart from the results; one\n // in a premise section is a premise nothing checks.\n const errors = (document.errors ?? []).filter((error) => error.spec !== undefined);\n const inBrief = (spec: { readonly file: string } | undefined, file: string): boolean => spec?.file.replace(/\\\\/g, '/') === file;\n const findings: Finding[] = [];\n let premises = 0;\n for (const brief of live) {\n const own = results.filter((result) => inBrief(result.spec, brief.file));\n const unread = errors.filter((error) => inBrief(error.spec, brief.file));\n if (own.length === 0 && unread.length === 0) continue;\n const text = await briefText(workspace, brief);\n const premise = (line: number): boolean => isPremise(reader.sectionsAt(text, line), workspace.config.assertions.premises);\n for (const result of own) {\n const line = result.spec?.line ?? 0;\n if (!premise(line)) continue;\n premises += 1;\n if (result.ok) continue;\n findings.push(premiseFinding(brief, { description: result.description, message: result.message, line }, brief.file === round));\n }\n for (const error of unread) {\n const line = error.spec?.line ?? 0;\n if (premise(line)) findings.push(unreadablePremiseFinding(brief, { message: error.message, line, raw: error.raw ?? '' }));\n }\n }\n const stale = findings.filter((finding) => finding.severity === 'error').length;\n const retired = findings.filter((finding) => finding.severity === 'note').length;\n const unreadable = findings.filter((finding) => finding.severity === 'warning').length;\n const intended = retired === 0 || active.kind !== 'found' ? '' : `, and ${retired} retired by the round on brief ${active.brief.id}, as it intends`;\n const unchecked = unreadable === 0 ? '' : `; ${unreadable} premise(s) spec-guard cannot read, so not checked`;\n const summary = `${premises} premise(s) in ${live.length} live brief(s), ${stale} no longer hold${intended}${unchecked}`;\n // As the audit does: a warning fails the run under --strict.\n const failed = stale > 0 || (options.strict && unreadable > 0);\n if (options.format === 'json') {\n io.stdout.write(json('premises', { ok: !failed, briefs: live.length, premises, unreadable, findings }));\n } else if (options.format === 'pretty') {\n for (const finding of findings) io.stdout.write(`${finding.severity.padEnd(8)} ${finding.file}:${finding.line} ${finding.message}\\n ${finding.hint}\\n`);\n io.stdout.write(`${summary}\\n`);\n } else {\n io.stdout.write(formatFindings(options.format, findings, { file: CONFIG_FILE, version: version(), summary }));\n }\n return failed ? EXIT_FAILED : EXIT_OK;\n}\n"]}
package/dist/reader.js CHANGED
@@ -2,7 +2,15 @@
2
2
  * The document reader, on spec-core's Markdown scanner: nothing inside code
3
3
  * or a comment is ever taken for a section, a table, a link or a probe.
4
4
  */
5
- import { readFrontMatter, scanMarkdown } from './vendor/spec-core/markdown/index.js';
5
+ import { findEntry, readFrontMatter, scanMarkdown } from './vendor/spec-core/markdown/index.js';
6
+ /**
7
+ * The front matter keys a cited document's status is read under, the first
8
+ * the document holds deciding: `status`, then the Traditional and the
9
+ * Simplified Chinese for it, which a Chinese ADR writes with YAML's ASCII
10
+ * colon. A full-width colon is no YAML separator, so `狀態:已接受` gives no
11
+ * key, and no status, as spec-guard reads it.
12
+ */
13
+ const STATUS_KEYS = ['status', '狀態', '状态'];
6
14
  /** Section names compare without case, emphasis, a leading number or a trailing colon. */
7
15
  export function sameSection(heading, name) {
8
16
  const normal = (text) => text
@@ -95,7 +103,7 @@ export function createReader() {
95
103
  const title = scanned.headings.find((heading) => heading.level === 1)?.text ?? null;
96
104
  const front = readFrontMatter(text);
97
105
  let status = null;
98
- const entry = front?.entries.find((candidate) => candidate.name === 'status');
106
+ const entry = STATUS_KEYS.map((key) => findEntry(front, key)).find((candidate) => candidate !== undefined);
99
107
  if (entry !== undefined && entry.value.kind === 'scalar')
100
108
  status = entry.value.scalar.text || null;
101
109
  // TOML front matter gives no status, closed or not, so closing it would
@@ -1 +1 @@
1
- {"version":3,"file":"reader.js","sourceRoot":"","sources":["../src/reader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The document reader, on spec-core's Markdown scanner: nothing inside code\n * or a comment is ever taken for a section, a table, a link or a probe.\n */\n\nimport type { Citation, DocumentReader, SectionTables } from './document.js';\nimport type { CodeBlock } from './probe.js';\nimport type { TableView } from './rulings.js';\nimport { readFrontMatter, scanMarkdown, type MarkdownScan } from './vendor/spec-core/markdown/index.js';\n\n/** Section names compare without case, emphasis, a leading number or a trailing colon. */\nexport function sameSection(heading: string, name: string): boolean {\n const normal = (text: string): string =>\n text\n .replace(/[*_`]/g, '')\n .replace(/^\\s*\\d+[.)]?\\s+/, '')\n .replace(/[:\\s]+$/, '')\n .trim()\n .toLowerCase();\n return normal(heading) === normal(name);\n}\n\nfunction scanOf(text: string, cache: { text: string; scan: MarkdownScan } | null): MarkdownScan {\n return cache !== null && cache.text === text ? cache.scan : scanMarkdown(text);\n}\n\nexport function createReader(): DocumentReader {\n // Commands read the same brief several times in a row; one scan serves them.\n let last: { text: string; scan: MarkdownScan } | null = null;\n const scan = (text: string): MarkdownScan => {\n const result = scanOf(text, last);\n last = { text, scan: result };\n return result;\n };\n\n return {\n codeBlocks(text: string): CodeBlock[] {\n const scanned = scan(text);\n const spaces = (line: string): number => (/^ */.exec(line) as RegExpExecArray)[0].length;\n return scanned.blocks\n .filter((block) => block.kind === 'fenced')\n .map((block) => {\n const first = block.line + 1;\n const last = block.closed ? block.endLine - 1 : block.endLine;\n // A fence in a block quote or a list item: its content is the code\n // without the quote markers and without the fence's own indent, as\n // CommonMark reads it, so a probe file is written as it was meant.\n const indent = spaces(scanned.lines[block.line - 1]?.content ?? '');\n const lines: string[] = [];\n for (let line = first; line <= last; line += 1) {\n const content = scanned.lines[line - 1]?.content ?? '';\n lines.push(content.slice(Math.min(indent, spaces(content))));\n }\n return { info: block.info, content: lines.join('\\n'), line: block.line };\n });\n },\n\n sectionAt(text: string, line: number): string | null {\n let found: string | null = null;\n for (const heading of scan(text).headings) {\n if (heading.line > line) break;\n if (heading.level >= 2) found = heading.text;\n }\n return found;\n },\n\n sectionsAt(text: string, line: number): string[] {\n const open: { level: number; text: string }[] = [];\n for (const heading of scan(text).headings) {\n if (heading.line > line) break;\n if (heading.level < 2) continue;\n while (open.length > 0 && (open[open.length - 1] as { level: number }).level >= heading.level) open.pop();\n open.push({ level: heading.level, text: heading.text });\n }\n return open.map((section) => section.text);\n },\n\n sectionTables(text: string, section: string): SectionTables {\n const scanned = scan(text);\n const headings = scanned.headings;\n const index = headings.findIndex((heading) => heading.level >= 2 && sameSection(heading.text, section));\n if (index < 0) return { tables: [], headingLine: null, tableEndLine: null };\n const heading = headings[index] as (typeof headings)[number];\n const next = headings.slice(index + 1).find((candidate) => candidate.level <= heading.level);\n const endLine = next === undefined ? scanned.index.lineCount + 1 : next.line;\n const tables = scanned.tables.filter((table) => table.line > heading.endLine && table.line < endLine);\n const views: TableView[] = tables.map((table) => ({\n headers: table.headers.map((cell) => cell.text),\n rows: table.rows.map((row) => ({ cells: row.cells.map((cell) => cell.text), line: row.line })),\n }));\n const lastTable = tables[tables.length - 1];\n return { tables: views, headingLine: heading.endLine, tableEndLine: lastTable === undefined ? null : lastTable.endLine };\n },\n\n citations(text: string): Citation[] {\n return scan(text)\n .links.filter((link) => !link.image && link.form !== 'autolink' && link.form !== 'definition')\n .map((link) => ({ target: link.target, line: link.line }));\n },\n\n titleAndStatus(text: string): { title: string | null; status: string | null; unclosedFrontMatter: boolean } {\n const scanned = scan(text);\n const title = scanned.headings.find((heading) => heading.level === 1)?.text ?? null;\n const front = readFrontMatter(text);\n let status: string | null = null;\n const entry = front?.entries.find((candidate) => candidate.name === 'status');\n if (entry !== undefined && entry.value.kind === 'scalar') status = entry.value.scalar.text || null;\n // TOML front matter gives no status, closed or not, so closing it would\n // change nothing the context packet shows; only YAML's is worth saying.\n return { title, status, unclosedFrontMatter: scanned.unclosedFrontMatter?.kind === 'yaml' };\n },\n };\n}\n"]}
1
+ {"version":3,"file":"reader.js","sourceRoot":"","sources":["../src/reader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The document reader, on spec-core's Markdown scanner: nothing inside code\n * or a comment is ever taken for a section, a table, a link or a probe.\n */\n\nimport type { Citation, DocumentReader, SectionTables } from './document.js';\nimport type { CodeBlock } from './probe.js';\nimport type { TableView } from './rulings.js';\nimport { findEntry, readFrontMatter, scanMarkdown, type MarkdownScan } from './vendor/spec-core/markdown/index.js';\n\n/**\n * The front matter keys a cited document's status is read under, the first\n * the document holds deciding: `status`, then the Traditional and the\n * Simplified Chinese for it, which a Chinese ADR writes with YAML's ASCII\n * colon. A full-width colon is no YAML separator, so `狀態:已接受` gives no\n * key, and no status, as spec-guard reads it.\n */\nconst STATUS_KEYS = ['status', '狀態', '状态'] as const;\n\n/** Section names compare without case, emphasis, a leading number or a trailing colon. */\nexport function sameSection(heading: string, name: string): boolean {\n const normal = (text: string): string =>\n text\n .replace(/[*_`]/g, '')\n .replace(/^\\s*\\d+[.)]?\\s+/, '')\n .replace(/[:\\s]+$/, '')\n .trim()\n .toLowerCase();\n return normal(heading) === normal(name);\n}\n\nfunction scanOf(text: string, cache: { text: string; scan: MarkdownScan } | null): MarkdownScan {\n return cache !== null && cache.text === text ? cache.scan : scanMarkdown(text);\n}\n\nexport function createReader(): DocumentReader {\n // Commands read the same brief several times in a row; one scan serves them.\n let last: { text: string; scan: MarkdownScan } | null = null;\n const scan = (text: string): MarkdownScan => {\n const result = scanOf(text, last);\n last = { text, scan: result };\n return result;\n };\n\n return {\n codeBlocks(text: string): CodeBlock[] {\n const scanned = scan(text);\n const spaces = (line: string): number => (/^ */.exec(line) as RegExpExecArray)[0].length;\n return scanned.blocks\n .filter((block) => block.kind === 'fenced')\n .map((block) => {\n const first = block.line + 1;\n const last = block.closed ? block.endLine - 1 : block.endLine;\n // A fence in a block quote or a list item: its content is the code\n // without the quote markers and without the fence's own indent, as\n // CommonMark reads it, so a probe file is written as it was meant.\n const indent = spaces(scanned.lines[block.line - 1]?.content ?? '');\n const lines: string[] = [];\n for (let line = first; line <= last; line += 1) {\n const content = scanned.lines[line - 1]?.content ?? '';\n lines.push(content.slice(Math.min(indent, spaces(content))));\n }\n return { info: block.info, content: lines.join('\\n'), line: block.line };\n });\n },\n\n sectionAt(text: string, line: number): string | null {\n let found: string | null = null;\n for (const heading of scan(text).headings) {\n if (heading.line > line) break;\n if (heading.level >= 2) found = heading.text;\n }\n return found;\n },\n\n sectionsAt(text: string, line: number): string[] {\n const open: { level: number; text: string }[] = [];\n for (const heading of scan(text).headings) {\n if (heading.line > line) break;\n if (heading.level < 2) continue;\n while (open.length > 0 && (open[open.length - 1] as { level: number }).level >= heading.level) open.pop();\n open.push({ level: heading.level, text: heading.text });\n }\n return open.map((section) => section.text);\n },\n\n sectionTables(text: string, section: string): SectionTables {\n const scanned = scan(text);\n const headings = scanned.headings;\n const index = headings.findIndex((heading) => heading.level >= 2 && sameSection(heading.text, section));\n if (index < 0) return { tables: [], headingLine: null, tableEndLine: null };\n const heading = headings[index] as (typeof headings)[number];\n const next = headings.slice(index + 1).find((candidate) => candidate.level <= heading.level);\n const endLine = next === undefined ? scanned.index.lineCount + 1 : next.line;\n const tables = scanned.tables.filter((table) => table.line > heading.endLine && table.line < endLine);\n const views: TableView[] = tables.map((table) => ({\n headers: table.headers.map((cell) => cell.text),\n rows: table.rows.map((row) => ({ cells: row.cells.map((cell) => cell.text), line: row.line })),\n }));\n const lastTable = tables[tables.length - 1];\n return { tables: views, headingLine: heading.endLine, tableEndLine: lastTable === undefined ? null : lastTable.endLine };\n },\n\n citations(text: string): Citation[] {\n return scan(text)\n .links.filter((link) => !link.image && link.form !== 'autolink' && link.form !== 'definition')\n .map((link) => ({ target: link.target, line: link.line }));\n },\n\n titleAndStatus(text: string): { title: string | null; status: string | null; unclosedFrontMatter: boolean } {\n const scanned = scan(text);\n const title = scanned.headings.find((heading) => heading.level === 1)?.text ?? null;\n const front = readFrontMatter(text);\n let status: string | null = null;\n const entry = STATUS_KEYS.map((key) => findEntry(front, key)).find((candidate) => candidate !== undefined);\n if (entry !== undefined && entry.value.kind === 'scalar') status = entry.value.scalar.text || null;\n // TOML front matter gives no status, closed or not, so closing it would\n // change nothing the context packet shows; only YAML's is worth saying.\n return { title, status, unclosedFrontMatter: scanned.unclosedFrontMatter?.kind === 'yaml' };\n },\n };\n}\n"]}
package/dist/round.d.ts CHANGED
@@ -45,6 +45,14 @@ export declare function briefText(workspace: Workspace, brief: BriefRow): Promis
45
45
  /** Decides paths against the brief, with the rulings verified against the base `baseFlag` names, or the configured one. */
46
46
  export declare function checkPaths(workspace: Workspace, brief: BriefRow | null, noBrief: string | undefined, given: readonly string[], cwd: string, reader: DocumentReader, baseFlag: string | undefined): Promise<Decision[]>;
47
47
  export declare function buildContext(workspace: Workspace, brief: BriefRow, briefs: readonly BriefRow[], reader: DocumentReader, baseFlag: string | undefined): Promise<ContextPacket>;
48
+ /**
49
+ * Whether a path spec-brief's `plugins` names, read from the root, is this
50
+ * package's plugin: the file its `./spec-brief-plugin` export names, as the
51
+ * project installed the package, or as this copy of it ships. spec-brief
52
+ * loads a plugin by a path as well as by its name, and a path to the plugin
53
+ * read as another plugin would say the archive cannot accept a ruling it can.
54
+ */
55
+ export declare function pluginFile(root: string): (path: string) => boolean;
48
56
  /** Whether spec-brief loads this package's plugin, as its configuration at the root says. */
49
57
  export declare function specBriefPlugin(root: string): Promise<PluginState>;
50
58
  /**