devworkspace 0.1.1 → 0.2.1

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 (123) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +15 -2
  3. package/dist/agents/claude/content.js +3 -3
  4. package/dist/agents/claude/content.js.map +1 -1
  5. package/dist/cli/args.d.ts +19 -0
  6. package/dist/cli/args.js +6 -0
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/help.js +40 -5
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/render.d.ts +7 -0
  11. package/dist/cli/render.js +104 -17
  12. package/dist/cli/render.js.map +1 -1
  13. package/dist/cli/run.d.ts +6 -0
  14. package/dist/cli/run.js +65 -3
  15. package/dist/cli/run.js.map +1 -1
  16. package/dist/config/schemas.d.ts +7 -1
  17. package/dist/config/schemas.js +10 -0
  18. package/dist/config/schemas.js.map +1 -1
  19. package/dist/core/workspace.js +2 -1
  20. package/dist/core/workspace.js.map +1 -1
  21. package/dist/discovery/commands.d.ts +34 -3
  22. package/dist/discovery/commands.js +95 -33
  23. package/dist/discovery/commands.js.map +1 -1
  24. package/dist/discovery/detect.d.ts +17 -0
  25. package/dist/discovery/detect.js +101 -42
  26. package/dist/discovery/detect.js.map +1 -1
  27. package/dist/index.d.ts +3 -1
  28. package/dist/index.js +2 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/integrations/adapters/agent-skills.d.ts +8 -0
  31. package/dist/integrations/adapters/agent-skills.js +25 -0
  32. package/dist/integrations/adapters/agent-skills.js.map +1 -0
  33. package/dist/integrations/adapters/claudePlugin.d.ts +41 -0
  34. package/dist/integrations/adapters/claudePlugin.js +129 -0
  35. package/dist/integrations/adapters/claudePlugin.js.map +1 -0
  36. package/dist/integrations/adapters/graphify.d.ts +2 -0
  37. package/dist/integrations/adapters/graphify.js +277 -0
  38. package/dist/integrations/adapters/graphify.js.map +1 -0
  39. package/dist/integrations/adapters/omniroute.d.ts +6 -0
  40. package/dist/integrations/adapters/omniroute.js +32 -0
  41. package/dist/integrations/adapters/omniroute.js.map +1 -0
  42. package/dist/integrations/adapters/pluginSkill.d.ts +28 -0
  43. package/dist/integrations/adapters/pluginSkill.js +195 -0
  44. package/dist/integrations/adapters/pluginSkill.js.map +1 -0
  45. package/dist/integrations/adapters/ponytail.d.ts +8 -0
  46. package/dist/integrations/adapters/ponytail.js +33 -0
  47. package/dist/integrations/adapters/ponytail.js.map +1 -0
  48. package/dist/integrations/exec.d.ts +26 -0
  49. package/dist/integrations/exec.js +167 -0
  50. package/dist/integrations/exec.js.map +1 -0
  51. package/dist/integrations/index.d.ts +5 -6
  52. package/dist/integrations/index.js +3 -29
  53. package/dist/integrations/index.js.map +1 -1
  54. package/dist/integrations/manager.d.ts +62 -0
  55. package/dist/integrations/manager.js +261 -0
  56. package/dist/integrations/manager.js.map +1 -0
  57. package/dist/integrations/notices.d.ts +13 -0
  58. package/dist/integrations/notices.js +68 -0
  59. package/dist/integrations/notices.js.map +1 -0
  60. package/dist/integrations/persistentPath.d.ts +20 -0
  61. package/dist/integrations/persistentPath.js +47 -0
  62. package/dist/integrations/persistentPath.js.map +1 -0
  63. package/dist/integrations/protect.d.ts +20 -0
  64. package/dist/integrations/protect.js +131 -0
  65. package/dist/integrations/protect.js.map +1 -0
  66. package/dist/integrations/state.d.ts +10 -0
  67. package/dist/integrations/state.js +81 -0
  68. package/dist/integrations/state.js.map +1 -0
  69. package/dist/integrations/types.d.ts +139 -16
  70. package/dist/integrations/types.js +13 -1
  71. package/dist/integrations/types.js.map +1 -1
  72. package/dist/prompts/onboarding.d.ts +3 -3
  73. package/dist/prompts/onboarding.js +9 -8
  74. package/dist/prompts/onboarding.js.map +1 -1
  75. package/dist/services/analysis.d.ts +15 -3
  76. package/dist/services/analysis.js +21 -23
  77. package/dist/services/analysis.js.map +1 -1
  78. package/dist/services/common.d.ts +8 -5
  79. package/dist/services/common.js +2 -5
  80. package/dist/services/common.js.map +1 -1
  81. package/dist/services/doctor.d.ts +10 -0
  82. package/dist/services/doctor.js +87 -19
  83. package/dist/services/doctor.js.map +1 -1
  84. package/dist/services/init.d.ts +9 -2
  85. package/dist/services/init.js +10 -7
  86. package/dist/services/init.js.map +1 -1
  87. package/dist/services/integrations.d.ts +26 -0
  88. package/dist/services/integrations.js +35 -0
  89. package/dist/services/integrations.js.map +1 -0
  90. package/dist/services/refresh.js +1 -1
  91. package/dist/services/refresh.js.map +1 -1
  92. package/dist/services/status.d.ts +4 -2
  93. package/dist/services/status.js +4 -4
  94. package/dist/services/status.js.map +1 -1
  95. package/dist/utils/lock.d.ts +2 -0
  96. package/dist/utils/lock.js +2 -2
  97. package/dist/utils/lock.js.map +1 -1
  98. package/dist/utils/proc.d.ts +8 -1
  99. package/dist/utils/proc.js +31 -8
  100. package/dist/utils/proc.js.map +1 -1
  101. package/docs/architecture.md +1 -1
  102. package/docs/claude.md +4 -0
  103. package/docs/getting-started.md +2 -2
  104. package/docs/integrations.md +98 -33
  105. package/docs/security.md +5 -1
  106. package/docs/testing.md +6 -0
  107. package/docs/troubleshooting.md +11 -1
  108. package/package.json +1 -1
  109. package/dist/integrations/agent-skills.d.ts +0 -10
  110. package/dist/integrations/agent-skills.js +0 -45
  111. package/dist/integrations/agent-skills.js.map +0 -1
  112. package/dist/integrations/graphify.d.ts +0 -7
  113. package/dist/integrations/graphify.js +0 -54
  114. package/dist/integrations/graphify.js.map +0 -1
  115. package/dist/integrations/omniroute.d.ts +0 -7
  116. package/dist/integrations/omniroute.js +0 -24
  117. package/dist/integrations/omniroute.js.map +0 -1
  118. package/dist/integrations/ponytail.d.ts +0 -10
  119. package/dist/integrations/ponytail.js +0 -58
  120. package/dist/integrations/ponytail.js.map +0 -1
  121. package/dist/integrations/probe.d.ts +0 -6
  122. package/dist/integrations/probe.js +0 -41
  123. package/dist/integrations/probe.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/config/schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { Check } from './validate.js';\n\nexport const SCHEMA_VERSION = 1;\n\nexport const CONFIDENCE = ['HIGH', 'MEDIUM', 'LOW'] as const;\nexport const FACT_STATUS = ['VERIFIED', 'DETECTED', 'INFERRED', 'USER_DECISION', 'UNKNOWN', 'STALE'] as const;\nexport const SOURCE_TYPES = ['detector', 'file', 'git', 'user', 'ai'] as const;\nexport const REVIEW_LEVELS = ['off', 'light', 'strict'] as const;\n\nexport type Confidence = (typeof CONFIDENCE)[number];\nexport type FactStatus = (typeof FACT_STATUS)[number];\nexport type SourceType = (typeof SOURCE_TYPES)[number];\nexport type ReviewLevel = (typeof REVIEW_LEVELS)[number];\n\nexport interface ManagedFile {\n path: string;\n mode: 'owned' | 'section';\n agent?: string;\n}\n\nexport interface ProjectConfig {\n schemaVersion: number;\n projectId: string;\n name: string;\n createdAt: string;\n updatedAt: string;\n devworkspaceVersion: string;\n settings: { review: ReviewLevel; maxFiles: number };\n managedFiles: ManagedFile[];\n}\n\nexport function validateProject(v: unknown, file = 'project.json'): ProjectConfig {\n const c = new Check(file);\n const o = c.obj(v);\n c.at_('schemaVersion').int(o.schemaVersion, 1);\n c.at_('projectId').str(o.projectId, { min: 8, max: 64 });\n c.at_('name').str(o.name, { min: 1, max: 200 });\n c.at_('createdAt').iso(o.createdAt);\n c.at_('updatedAt').iso(o.updatedAt);\n c.at_('devworkspaceVersion').str(o.devworkspaceVersion, { max: 40 });\n const s = c.at_('settings').obj(o.settings);\n c.at_('settings.review').oneOf(s.review, REVIEW_LEVELS);\n c.at_('settings.maxFiles').int(s.maxFiles, 100);\n const mf = c.at_('managedFiles').arr(o.managedFiles, 200);\n mf.forEach((m, i) => {\n const mc = c.at_(`managedFiles.${i}`);\n const mo = mc.obj(m);\n mc.at_('path').str(mo.path, { max: 240 });\n mc.at_('mode').oneOf(mo.mode, ['owned', 'section'] as const);\n });\n return o as unknown as ProjectConfig;\n}\n\nexport interface Fact {\n id: string;\n key: string;\n value: string;\n source: string;\n sourceType: SourceType;\n sources: string[];\n sourceHashes: Record<string, string>;\n confidence: Confidence;\n status: FactStatus;\n createdAt: string;\n updatedAt: string;\n lastVerifiedAt: string | null;\n staleReason?: string;\n}\n\nexport interface FactsFile {\n schemaVersion: number;\n facts: Fact[];\n}\n\nexport function validateFacts(v: unknown, file = 'facts.json'): FactsFile {\n const c = new Check(file);\n const o = c.obj(v);\n c.at_('schemaVersion').int(o.schemaVersion, 1);\n const facts = c.at_('facts').arr(o.facts, 5000);\n facts.forEach((f, i) => {\n const fc = c.at_(`facts.${i}`);\n const fo = fc.obj(f);\n fc.at_('id').str(fo.id, { max: 64 });\n fc.at_('key').str(fo.key, { min: 1, max: 100 });\n fc.at_('value').str(fo.value, { max: 4000 });\n fc.at_('source').str(fo.source, { max: 500 });\n fc.at_('sourceType').oneOf(fo.sourceType, SOURCE_TYPES);\n fc.at_('sources').strArr(fo.sources, 100);\n fc.at_('sourceHashes').obj(fo.sourceHashes);\n fc.at_('confidence').oneOf(fo.confidence, CONFIDENCE);\n fc.at_('status').oneOf(fo.status, FACT_STATUS);\n fc.at_('createdAt').iso(fo.createdAt);\n fc.at_('updatedAt').iso(fo.updatedAt);\n if (fo.lastVerifiedAt !== null) fc.at_('lastVerifiedAt').iso(fo.lastVerifiedAt);\n });\n return o as unknown as FactsFile;\n}\n\nexport const CHECK_IDS = ['typecheck', 'unit', 'integration', 'e2e', 'lint', 'format', 'build', 'migrations', 'security'] as const;\nexport type CheckId = (typeof CHECK_IDS)[number];\nexport const REQUIRED_LEVELS = ['yes', 'context', 'no'] as const;\n\nexport interface CommandEntry {\n id: CheckId;\n available: boolean;\n command: string | null;\n required: (typeof REQUIRED_LEVELS)[number];\n source: string | null;\n /** 'repository-script' = defined by repo config (untrusted input); 'inferred' = guessed from tooling; 'user' = set by a human. */\n origin: 'repository-script' | 'inferred' | 'user';\n}\n\nexport interface CommandsFile {\n schemaVersion: number;\n checks: CommandEntry[];\n trustNote: string;\n}\n\nexport function validateCommands(v: unknown, file = 'commands.json'): CommandsFile {\n const c = new Check(file);\n const o = c.obj(v);\n c.at_('schemaVersion').int(o.schemaVersion, 1);\n c.at_('trustNote').str(o.trustNote);\n c.at_('checks')\n .arr(o.checks, 100)\n .forEach((e, i) => {\n const ec = c.at_(`checks.${i}`);\n const eo = ec.obj(e);\n ec.at_('id').oneOf(eo.id, CHECK_IDS);\n ec.at_('available').bool(eo.available);\n if (eo.command !== null) ec.at_('command').str(eo.command, { max: 500 });\n ec.at_('required').oneOf(eo.required, REQUIRED_LEVELS);\n ec.at_('origin').oneOf(eo.origin, ['repository-script', 'inferred', 'user'] as const);\n });\n return o as unknown as CommandsFile;\n}\n\nexport interface AnalysisState {\n schemaVersion: number;\n analyzedAt: string;\n refreshedAt: string | null;\n gitHead: string | null;\n fileCount: number;\n truncated: boolean;\n discoveryHash: string;\n}\n\nexport function validateAnalysisState(v: unknown, file = 'analysis.json'): AnalysisState {\n const c = new Check(file);\n const o = c.obj(v);\n c.at_('schemaVersion').int(o.schemaVersion, 1);\n c.at_('analyzedAt').iso(o.analyzedAt);\n c.at_('fileCount').int(o.fileCount);\n c.at_('truncated').bool(o.truncated);\n c.at_('discoveryHash').str(o.discoveryHash, { max: 100 });\n return o as unknown as AnalysisState;\n}\n"]}
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/config/schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { DevWorkspaceError } from '../errors/index.js';\nimport { validateRelativePath } from '../utils/paths.js';\nimport { Check } from './validate.js';\n\nexport const SCHEMA_VERSION = 1;\n\nexport const CONFIDENCE = ['HIGH', 'MEDIUM', 'LOW'] as const;\nexport const FACT_STATUS = ['VERIFIED', 'DETECTED', 'INFERRED', 'USER_DECISION', 'UNKNOWN', 'STALE'] as const;\nexport const SOURCE_TYPES = ['detector', 'file', 'git', 'user', 'ai'] as const;\nexport const REVIEW_LEVELS = ['off', 'light', 'strict'] as const;\n\nexport type Confidence = (typeof CONFIDENCE)[number];\nexport type FactStatus = (typeof FACT_STATUS)[number];\nexport type SourceType = (typeof SOURCE_TYPES)[number];\nexport type ReviewLevel = (typeof REVIEW_LEVELS)[number];\n\nexport interface ManagedFile {\n path: string;\n mode: 'owned' | 'section';\n agent?: string;\n}\n\nexport interface ProjectConfig {\n schemaVersion: number;\n projectId: string;\n name: string;\n createdAt: string;\n updatedAt: string;\n devworkspaceVersion: string;\n settings: { review: ReviewLevel; maxFiles: number; /** The project intentionally has no tests/lint/build; doctor stops warning about it. */ acknowledgeNoVerification?: boolean };\n managedFiles: ManagedFile[];\n}\n\nexport function validateProject(v: unknown, file = 'project.json'): ProjectConfig {\n const c = new Check(file);\n const o = c.obj(v);\n c.at_('schemaVersion').int(o.schemaVersion, 1);\n c.at_('projectId').str(o.projectId, { min: 8, max: 64 });\n c.at_('name').str(o.name, { min: 1, max: 200 });\n c.at_('createdAt').iso(o.createdAt);\n c.at_('updatedAt').iso(o.updatedAt);\n c.at_('devworkspaceVersion').str(o.devworkspaceVersion, { max: 40 });\n const s = c.at_('settings').obj(o.settings);\n c.at_('settings.review').oneOf(s.review, REVIEW_LEVELS);\n c.at_('settings.maxFiles').int(s.maxFiles, 100);\n if (s.acknowledgeNoVerification !== undefined) c.at_('settings.acknowledgeNoVerification').bool(s.acknowledgeNoVerification);\n const mf = c.at_('managedFiles').arr(o.managedFiles, 200);\n mf.forEach((m, i) => {\n const mc = c.at_(`managedFiles.${i}`);\n const mo = mc.obj(m);\n mc.at_('path').str(mo.path, { max: 240 });\n mc.at_('mode').oneOf(mo.mode, ['owned', 'section'] as const);\n });\n return o as unknown as ProjectConfig;\n}\n\nexport interface Fact {\n id: string;\n key: string;\n value: string;\n source: string;\n sourceType: SourceType;\n sources: string[];\n sourceHashes: Record<string, string>;\n confidence: Confidence;\n status: FactStatus;\n createdAt: string;\n updatedAt: string;\n lastVerifiedAt: string | null;\n staleReason?: string;\n}\n\nexport interface FactsFile {\n schemaVersion: number;\n facts: Fact[];\n}\n\nexport function validateFacts(v: unknown, file = 'facts.json'): FactsFile {\n const c = new Check(file);\n const o = c.obj(v);\n c.at_('schemaVersion').int(o.schemaVersion, 1);\n const facts = c.at_('facts').arr(o.facts, 5000);\n facts.forEach((f, i) => {\n const fc = c.at_(`facts.${i}`);\n const fo = fc.obj(f);\n fc.at_('id').str(fo.id, { max: 64 });\n fc.at_('key').str(fo.key, { min: 1, max: 100 });\n fc.at_('value').str(fo.value, { max: 4000 });\n fc.at_('source').str(fo.source, { max: 500 });\n fc.at_('sourceType').oneOf(fo.sourceType, SOURCE_TYPES);\n fc.at_('sources').strArr(fo.sources, 100);\n fc.at_('sourceHashes').obj(fo.sourceHashes);\n fc.at_('confidence').oneOf(fo.confidence, CONFIDENCE);\n fc.at_('status').oneOf(fo.status, FACT_STATUS);\n fc.at_('createdAt').iso(fo.createdAt);\n fc.at_('updatedAt').iso(fo.updatedAt);\n if (fo.lastVerifiedAt !== null) fc.at_('lastVerifiedAt').iso(fo.lastVerifiedAt);\n });\n return o as unknown as FactsFile;\n}\n\nexport const CHECK_IDS = ['typecheck', 'unit', 'integration', 'e2e', 'lint', 'format', 'build', 'migrations', 'security'] as const;\nexport type CheckId = (typeof CHECK_IDS)[number];\nexport const REQUIRED_LEVELS = ['yes', 'context', 'no'] as const;\n\nexport interface CommandEntry {\n id: CheckId;\n available: boolean;\n command: string | null;\n required: (typeof REQUIRED_LEVELS)[number];\n source: string | null;\n /** 'repository-script' = defined by repo config (untrusted input); 'inferred' = guessed from tooling; 'user' = set by a human. */\n origin: 'repository-script' | 'inferred' | 'user';\n /**\n * Working directory, relative to the project root (POSIX separators). Absent means the project root.\n * Package-specific commands (backend vs frontend) must carry it so they are never run in the wrong place.\n */\n cwd?: string;\n}\n\nexport interface CommandsFile {\n schemaVersion: number;\n checks: CommandEntry[];\n trustNote: string;\n}\n\nexport function validateCommands(v: unknown, file = 'commands.json'): CommandsFile {\n const c = new Check(file);\n const o = c.obj(v);\n c.at_('schemaVersion').int(o.schemaVersion, 1);\n c.at_('trustNote').str(o.trustNote);\n c.at_('checks')\n .arr(o.checks, 100)\n .forEach((e, i) => {\n const ec = c.at_(`checks.${i}`);\n const eo = ec.obj(e);\n ec.at_('id').oneOf(eo.id, CHECK_IDS);\n ec.at_('available').bool(eo.available);\n if (eo.command !== null) ec.at_('command').str(eo.command, { max: 500 });\n ec.at_('required').oneOf(eo.required, REQUIRED_LEVELS);\n ec.at_('origin').oneOf(eo.origin, ['repository-script', 'inferred', 'user'] as const);\n if (eo.cwd !== undefined) {\n const cwd = ec.at_('cwd').str(eo.cwd, { max: 240 });\n const err = validateRelativePath(cwd);\n if (err) throw new DevWorkspaceError('SCHEMA', `Invalid ${file}: checks.${i}.cwd ${err}.`);\n }\n });\n return o as unknown as CommandsFile;\n}\n\nexport interface AnalysisState {\n schemaVersion: number;\n analyzedAt: string;\n refreshedAt: string | null;\n gitHead: string | null;\n fileCount: number;\n truncated: boolean;\n discoveryHash: string;\n}\n\nexport function validateAnalysisState(v: unknown, file = 'analysis.json'): AnalysisState {\n const c = new Check(file);\n const o = c.obj(v);\n c.at_('schemaVersion').int(o.schemaVersion, 1);\n c.at_('analyzedAt').iso(o.analyzedAt);\n c.at_('fileCount').int(o.fileCount);\n c.at_('truncated').bool(o.truncated);\n c.at_('discoveryHash').str(o.discoveryHash, { max: 100 });\n return o as unknown as AnalysisState;\n}\n"]}
@@ -26,7 +26,7 @@ export function wsPaths(root) {
26
26
  stateDir: j('state'),
27
27
  analysisState: j('state', 'analysis.json'),
28
28
  localDir: j('local'),
29
- integrationsState: j('local', 'integrations.json'),
29
+ integrationsState: j('state', 'integrations.json'),
30
30
  backupsDir: j('local', 'backups'),
31
31
  gitignore: j('.gitignore'),
32
32
  };
@@ -86,6 +86,7 @@ export const LOCAL_GITIGNORE = [
86
86
  'local/',
87
87
  'analysis/onboarding-prompt.md',
88
88
  'analysis/refresh-prompt.md',
89
+ 'state/integrations.json',
89
90
  '*.tmp',
90
91
  '',
91
92
  ].join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/core/workspace.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { DevWorkspaceError } from '../errors/index.js';\nimport { atomicWriteJson, ensureDir, pathExists, readJsonFile } from '../utils/fs.js';\nimport { validateProject, type ProjectConfig, SCHEMA_VERSION } from '../config/schemas.js';\nimport { MIGRATIONS, migrateData, type Migration } from './migrations.js';\n\nexport const WORKSPACE_DIR = '.devworkspace';\n\nexport interface Ws {\n root: string;\n dir: string;\n projectJson: string;\n contextDir: string;\n facts: string;\n commands: string;\n decisionsDir: string;\n tasksActive: string;\n tasksCompleted: string;\n sessionsDir: string;\n analysisDir: string;\n discovery: string;\n onboardingPrompt: string;\n refreshPrompt: string;\n stateDir: string;\n analysisState: string;\n localDir: string;\n integrationsState: string;\n backupsDir: string;\n gitignore: string;\n}\n\nexport function wsPaths(root: string): Ws {\n const dir = path.join(root, WORKSPACE_DIR);\n const j = (...p: string[]): string => path.join(dir, ...p);\n return {\n root,\n dir,\n projectJson: j('project.json'),\n contextDir: j('context'),\n facts: j('context', 'facts.json'),\n commands: j('context', 'commands.json'),\n decisionsDir: j('decisions'),\n tasksActive: j('tasks', 'active'),\n tasksCompleted: j('tasks', 'completed'),\n sessionsDir: j('sessions'),\n analysisDir: j('analysis'),\n discovery: j('analysis', 'discovery.json'),\n onboardingPrompt: j('analysis', 'onboarding-prompt.md'),\n refreshPrompt: j('analysis', 'refresh-prompt.md'),\n stateDir: j('state'),\n analysisState: j('state', 'analysis.json'),\n localDir: j('local'),\n integrationsState: j('local', 'integrations.json'),\n backupsDir: j('local', 'backups'),\n gitignore: j('.gitignore'),\n };\n}\n\n/** Walk upward from start looking for an initialised workspace. The workspace location is never stored in it. */\nexport async function findWorkspaceRoot(start: string): Promise<string | null> {\n let dir = path.resolve(start);\n for (;;) {\n if (await pathExists(path.join(dir, WORKSPACE_DIR, 'project.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport async function requireWorkspace(cwd: string): Promise<Ws> {\n const root = await findWorkspaceRoot(cwd);\n if (!root) {\n throw new DevWorkspaceError('NOT_INITIALIZED', 'No DevWorkspace found in this directory or any parent.', {\n hint: 'Run \"devworkspace init\" in the project root.',\n });\n }\n return wsPaths(root);\n}\n\nexport async function loadProject(ws: Ws, migrations: Migration[] = MIGRATIONS.project ?? []): Promise<ProjectConfig> {\n const raw = await readJsonFile(ws.projectJson);\n const { data } = migrateData(raw, SCHEMA_VERSION, migrations, 'project.json');\n return validateProject(data);\n}\n\nexport async function saveProject(ws: Ws, cfg: ProjectConfig): Promise<void> {\n validateProject(cfg);\n await backupIfVersionChanges(ws, ws.projectJson, cfg.schemaVersion);\n await atomicWriteJson(ws.projectJson, cfg);\n}\n\n/** Before a write that upgrades a file's schemaVersion on disk, keep a copy of the old file. */\nexport async function backupIfVersionChanges(ws: Ws, file: string, newVersion: number, stamp?: string): Promise<string | null> {\n if (!(await pathExists(file))) return null;\n let onDisk: unknown;\n try {\n onDisk = ((await readJsonFile(file)) as { schemaVersion?: unknown }).schemaVersion;\n } catch {\n onDisk = undefined;\n }\n if (onDisk === newVersion) return null;\n const folder = stamp ?? new Date().toISOString().replace(/[:.]/g, '-');\n const dest = path.join(ws.backupsDir, folder, path.relative(ws.dir, file));\n await ensureDir(path.dirname(dest));\n await fs.copyFile(file, dest);\n return dest;\n}\n\nexport const LOCAL_GITIGNORE = [\n '# Managed by DevWorkspace. Machine-local state and regenerable output.',\n 'local/',\n 'analysis/onboarding-prompt.md',\n 'analysis/refresh-prompt.md',\n '*.tmp',\n '',\n].join('\\n');\n"]}
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/core/workspace.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { DevWorkspaceError } from '../errors/index.js';\nimport { atomicWriteJson, ensureDir, pathExists, readJsonFile } from '../utils/fs.js';\nimport { validateProject, type ProjectConfig, SCHEMA_VERSION } from '../config/schemas.js';\nimport { MIGRATIONS, migrateData, type Migration } from './migrations.js';\n\nexport const WORKSPACE_DIR = '.devworkspace';\n\nexport interface Ws {\n root: string;\n dir: string;\n projectJson: string;\n contextDir: string;\n facts: string;\n commands: string;\n decisionsDir: string;\n tasksActive: string;\n tasksCompleted: string;\n sessionsDir: string;\n analysisDir: string;\n discovery: string;\n onboardingPrompt: string;\n refreshPrompt: string;\n stateDir: string;\n analysisState: string;\n localDir: string;\n integrationsState: string;\n backupsDir: string;\n gitignore: string;\n}\n\nexport function wsPaths(root: string): Ws {\n const dir = path.join(root, WORKSPACE_DIR);\n const j = (...p: string[]): string => path.join(dir, ...p);\n return {\n root,\n dir,\n projectJson: j('project.json'),\n contextDir: j('context'),\n facts: j('context', 'facts.json'),\n commands: j('context', 'commands.json'),\n decisionsDir: j('decisions'),\n tasksActive: j('tasks', 'active'),\n tasksCompleted: j('tasks', 'completed'),\n sessionsDir: j('sessions'),\n analysisDir: j('analysis'),\n discovery: j('analysis', 'discovery.json'),\n onboardingPrompt: j('analysis', 'onboarding-prompt.md'),\n refreshPrompt: j('analysis', 'refresh-prompt.md'),\n stateDir: j('state'),\n analysisState: j('state', 'analysis.json'),\n localDir: j('local'),\n integrationsState: j('state', 'integrations.json'),\n backupsDir: j('local', 'backups'),\n gitignore: j('.gitignore'),\n };\n}\n\n/** Walk upward from start looking for an initialised workspace. The workspace location is never stored in it. */\nexport async function findWorkspaceRoot(start: string): Promise<string | null> {\n let dir = path.resolve(start);\n for (;;) {\n if (await pathExists(path.join(dir, WORKSPACE_DIR, 'project.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport async function requireWorkspace(cwd: string): Promise<Ws> {\n const root = await findWorkspaceRoot(cwd);\n if (!root) {\n throw new DevWorkspaceError('NOT_INITIALIZED', 'No DevWorkspace found in this directory or any parent.', {\n hint: 'Run \"devworkspace init\" in the project root.',\n });\n }\n return wsPaths(root);\n}\n\nexport async function loadProject(ws: Ws, migrations: Migration[] = MIGRATIONS.project ?? []): Promise<ProjectConfig> {\n const raw = await readJsonFile(ws.projectJson);\n const { data } = migrateData(raw, SCHEMA_VERSION, migrations, 'project.json');\n return validateProject(data);\n}\n\nexport async function saveProject(ws: Ws, cfg: ProjectConfig): Promise<void> {\n validateProject(cfg);\n await backupIfVersionChanges(ws, ws.projectJson, cfg.schemaVersion);\n await atomicWriteJson(ws.projectJson, cfg);\n}\n\n/** Before a write that upgrades a file's schemaVersion on disk, keep a copy of the old file. */\nexport async function backupIfVersionChanges(ws: Ws, file: string, newVersion: number, stamp?: string): Promise<string | null> {\n if (!(await pathExists(file))) return null;\n let onDisk: unknown;\n try {\n onDisk = ((await readJsonFile(file)) as { schemaVersion?: unknown }).schemaVersion;\n } catch {\n onDisk = undefined;\n }\n if (onDisk === newVersion) return null;\n const folder = stamp ?? new Date().toISOString().replace(/[:.]/g, '-');\n const dest = path.join(ws.backupsDir, folder, path.relative(ws.dir, file));\n await ensureDir(path.dirname(dest));\n await fs.copyFile(file, dest);\n return dest;\n}\n\nexport const LOCAL_GITIGNORE = [\n '# Managed by DevWorkspace. Machine-local state and regenerable output.',\n 'local/',\n 'analysis/onboarding-prompt.md',\n 'analysis/refresh-prompt.md',\n 'state/integrations.json',\n '*.tmp',\n '',\n].join('\\n');\n"]}
@@ -1,12 +1,43 @@
1
- import type { CommandEntry } from '../config/schemas.js';
1
+ import type { CheckId, CommandEntry } from '../config/schemas.js';
2
2
  import type { PackageInfo } from './detect.js';
3
3
  export declare const TRUST_NOTE: string;
4
+ /** One Node.js package: the manifest, where it lives, and which package manager its own lockfile says it uses. */
5
+ export interface NodePackageInput {
6
+ /** Directory relative to the project root, POSIX separators; '' for the root. */
7
+ dir: string;
8
+ pkg: PackageInfo;
9
+ packageManager: string;
10
+ hasTsconfig: boolean;
11
+ }
4
12
  export interface ContractInput {
5
- pkg: PackageInfo | null;
6
- packageManager: string | null;
13
+ nodePackages: NodePackageInput[];
7
14
  ecosystems: Set<string>;
8
15
  has: (rel: string) => boolean;
9
16
  pythonEvidence: Record<string, string>;
10
17
  files: Set<string>;
11
18
  }
19
+ export declare const CHECK_ORDER: CheckId[];
20
+ /**
21
+ * Detected verification commands. Node commands are produced per package and only for scripts that exist in that
22
+ * package's own manifest. Every check that no package provides gets one unavailable entry so the contract always
23
+ * lists all nine checks.
24
+ */
12
25
  export declare function buildContract(input: ContractInput): CommandEntry[];
26
+ export interface MergeResult {
27
+ checks: CommandEntry[];
28
+ /** Entries kept from the previous contract because a human confirmed them (origin "user"). */
29
+ preservedUser: number;
30
+ /** Previously detected entries kept because this analysis found nothing or was cut short. */
31
+ preservedStale: number;
32
+ /** The new detection found no verification commands at all, or the scan was truncated. */
33
+ incomplete: boolean;
34
+ }
35
+ /**
36
+ * Combine the previous contract with fresh detection.
37
+ * - Entries confirmed by a person (origin "user") are never dropped or overwritten. A user entry without a cwd
38
+ * covers its whole check; one with a cwd covers only that directory, and detection fills in the other packages.
39
+ * - If detection is incomplete, previously available entries survive too, with their original provenance.
40
+ */
41
+ export declare function mergeContract(previous: CommandEntry[] | null, detected: CommandEntry[], opts?: {
42
+ truncated?: boolean;
43
+ }): MergeResult;
@@ -1,13 +1,16 @@
1
1
  export const TRUST_NOTE = 'Commands marked "repository-script" come from files in the repository (e.g. package.json scripts) and are untrusted input. ' +
2
2
  'Inspect what a script does before running it, especially in automation or on unfamiliar repositories.';
3
+ /** First match wins, so more specific scripts (test:unit) are preferred over catch-alls (test). */
3
4
  const SCRIPT_CANDIDATES = {
4
5
  typecheck: ['typecheck', 'type-check', 'check-types', 'tsc', 'types'],
5
- unit: ['test', 'test:unit', 'unit', 'test:run'],
6
+ unit: ['test:unit', 'unit', 'test', 'test:run'],
6
7
  integration: ['test:integration', 'test:int', 'integration'],
7
8
  e2e: ['test:e2e', 'e2e', 'test:playwright', 'test:cypress', 'cypress:run'],
8
9
  lint: ['lint', 'eslint', 'lint:check'],
9
10
  format: ['format:check', 'prettier:check', 'format-check', 'check:format'],
10
11
  build: ['build', 'compile'],
12
+ migrations: ['db:migrate', 'migrate'],
13
+ security: ['test:security', 'security:test', 'security', 'audit:security'],
11
14
  };
12
15
  const REQUIRED = {
13
16
  typecheck: 'yes',
@@ -20,73 +23,132 @@ const REQUIRED = {
20
23
  migrations: 'context',
21
24
  security: 'context',
22
25
  };
23
- const ORDER = ['typecheck', 'unit', 'integration', 'e2e', 'lint', 'format', 'build', 'migrations', 'security'];
26
+ export const CHECK_ORDER = ['typecheck', 'unit', 'integration', 'e2e', 'lint', 'format', 'build', 'migrations', 'security'];
24
27
  function scriptCommand(pm, script) {
25
- const mgr = pm ?? 'npm';
26
- if (script === 'test' && (mgr === 'npm' || mgr === 'pnpm' || mgr === 'yarn' || mgr === 'bun'))
27
- return `${mgr} test`;
28
- if (mgr === 'yarn')
28
+ if (script === 'test' && (pm === 'npm' || pm === 'pnpm' || pm === 'yarn' || pm === 'bun'))
29
+ return `${pm} test`;
30
+ if (pm === 'yarn')
29
31
  return `yarn ${script}`;
30
- if (mgr === 'bun')
32
+ if (pm === 'bun')
31
33
  return `bun run ${script}`;
32
- return `${mgr} run ${script}`;
34
+ return `${pm} run ${script}`;
33
35
  }
34
36
  /** Placeholder scripts such as npm's default "no test specified" do not count as tests. */
35
37
  function isRealScript(body) {
36
38
  return !/no test specified/i.test(body) && body.trim() !== '';
37
39
  }
40
+ const byCwd = (a, b) => (a.cwd ?? '').localeCompare(b.cwd ?? '');
41
+ /**
42
+ * Detected verification commands. Node commands are produced per package and only for scripts that exist in that
43
+ * package's own manifest. Every check that no package provides gets one unavailable entry so the contract always
44
+ * lists all nine checks.
45
+ */
38
46
  export function buildContract(input) {
39
- const found = new Map();
40
- const set = (id, command, source, origin) => {
41
- if (!found.has(id))
42
- found.set(id, { id, available: true, command, required: REQUIRED[id], source, origin });
47
+ const found = [];
48
+ const add = (id, command, source, origin, cwd) => {
49
+ if (!found.some((f) => f.id === id && (f.cwd ?? '') === (cwd ?? ''))) {
50
+ found.push({ id, available: true, command, required: REQUIRED[id], source, origin, ...(cwd ? { cwd } : {}) });
51
+ }
43
52
  };
44
- if (input.pkg) {
53
+ for (const np of input.nodePackages) {
54
+ const manifest = np.dir ? `${np.dir}/package.json` : 'package.json';
45
55
  for (const [id, names] of Object.entries(SCRIPT_CANDIDATES)) {
46
56
  for (const name of names) {
47
- const body = input.pkg.scripts[name];
57
+ const body = np.pkg.scripts[name];
48
58
  if (body !== undefined && isRealScript(body)) {
49
- set(id, scriptCommand(input.packageManager, name), `${input.pkg.rel}#scripts.${name}`, 'repository-script');
59
+ add(id, scriptCommand(np.packageManager, name), `${manifest}#scripts.${name}`, 'repository-script', np.dir);
50
60
  break;
51
61
  }
52
62
  }
53
63
  }
54
- if (!found.has('typecheck') && input.has('tsconfig.json') && input.pkg.hasTypes) {
55
- set('typecheck', `${input.packageManager === 'pnpm' ? 'pnpm exec' : 'npx'} tsc --noEmit`, 'tsconfig.json', 'inferred');
64
+ if (np.pkg.scripts.typecheck === undefined && np.hasTsconfig && np.pkg.hasTypes && !found.some((f) => f.id === 'typecheck' && (f.cwd ?? '') === np.dir)) {
65
+ add('typecheck', `${np.packageManager === 'pnpm' ? 'pnpm exec' : 'npx'} tsc --noEmit`, `${np.dir ? `${np.dir}/` : ''}tsconfig.json`, 'inferred', np.dir);
56
66
  }
57
67
  }
58
68
  if (input.ecosystems.has('python')) {
59
69
  const ev = input.pythonEvidence;
60
70
  const hasTestsDir = [...input.files].some((f) => /^tests?\//.test(f) && f.endsWith('.py'));
61
71
  if (ev.pytest || hasTestsDir || input.has('pytest.ini'))
62
- set('unit', 'pytest', ev.pytest ?? 'tests/', 'inferred');
72
+ add('unit', 'pytest', ev.pytest ?? 'tests/', 'inferred');
63
73
  if (ev.ruff)
64
- set('lint', 'ruff check .', ev.ruff, 'inferred');
74
+ add('lint', 'ruff check .', ev.ruff, 'inferred');
65
75
  if (ev.mypy)
66
- set('typecheck', 'mypy .', ev.mypy, 'inferred');
76
+ add('typecheck', 'mypy .', ev.mypy, 'inferred');
67
77
  if (ev.black)
68
- set('format', 'black --check .', ev.black, 'inferred');
78
+ add('format', 'black --check .', ev.black, 'inferred');
69
79
  if (ev.alembic)
70
- set('migrations', 'alembic check', ev.alembic, 'inferred');
80
+ add('migrations', 'alembic check', ev.alembic, 'inferred');
71
81
  }
72
82
  if (input.ecosystems.has('go')) {
73
- set('unit', 'go test ./...', 'go.mod', 'inferred');
74
- set('build', 'go build ./...', 'go.mod', 'inferred');
75
- set('lint', 'go vet ./...', 'go.mod', 'inferred');
83
+ add('unit', 'go test ./...', 'go.mod', 'inferred');
84
+ add('build', 'go build ./...', 'go.mod', 'inferred');
85
+ add('lint', 'go vet ./...', 'go.mod', 'inferred');
76
86
  }
77
87
  if (input.ecosystems.has('rust')) {
78
- set('unit', 'cargo test', 'Cargo.toml', 'inferred');
79
- set('build', 'cargo build', 'Cargo.toml', 'inferred');
80
- set('lint', 'cargo clippy', 'Cargo.toml', 'inferred');
88
+ add('unit', 'cargo test', 'Cargo.toml', 'inferred');
89
+ add('build', 'cargo build', 'Cargo.toml', 'inferred');
90
+ add('lint', 'cargo clippy', 'Cargo.toml', 'inferred');
81
91
  }
82
92
  if (input.has('pom.xml')) {
83
- set('unit', 'mvn test', 'pom.xml', 'inferred');
84
- set('build', 'mvn package', 'pom.xml', 'inferred');
93
+ add('unit', 'mvn test', 'pom.xml', 'inferred');
94
+ add('build', 'mvn package', 'pom.xml', 'inferred');
85
95
  }
86
96
  if (input.has('build.gradle') || input.has('build.gradle.kts')) {
87
- set('unit', 'gradle test', 'build.gradle', 'inferred');
88
- set('build', 'gradle build', 'build.gradle', 'inferred');
97
+ add('unit', 'gradle test', 'build.gradle', 'inferred');
98
+ add('build', 'gradle build', 'build.gradle', 'inferred');
99
+ }
100
+ return withPlaceholders(found);
101
+ }
102
+ function withPlaceholders(entries) {
103
+ const out = [];
104
+ for (const id of CHECK_ORDER) {
105
+ const mine = entries.filter((e) => e.id === id && e.available).sort(byCwd);
106
+ if (mine.length > 0)
107
+ out.push(...mine);
108
+ else {
109
+ const unavailable = entries.find((e) => e.id === id);
110
+ out.push(unavailable ?? { id, available: false, command: null, required: 'no', source: null, origin: 'inferred' });
111
+ }
112
+ }
113
+ return out;
114
+ }
115
+ /**
116
+ * Combine the previous contract with fresh detection.
117
+ * - Entries confirmed by a person (origin "user") are never dropped or overwritten. A user entry without a cwd
118
+ * covers its whole check; one with a cwd covers only that directory, and detection fills in the other packages.
119
+ * - If detection is incomplete, previously available entries survive too, with their original provenance.
120
+ */
121
+ export function mergeContract(previous, detected, opts = {}) {
122
+ const prev = previous ?? [];
123
+ const detectedAvailable = detected.filter((d) => d.available);
124
+ const incomplete = detectedAvailable.length === 0 || !!opts.truncated;
125
+ let preservedUser = 0;
126
+ let preservedStale = 0;
127
+ const out = [];
128
+ for (const id of CHECK_ORDER) {
129
+ const user = prev.filter((p) => p.id === id && p.origin === 'user' && p.available);
130
+ const globalUser = user.some((u) => !u.cwd);
131
+ const det = detectedAvailable.filter((d) => d.id === id);
132
+ const chosen = [...user];
133
+ preservedUser += user.length;
134
+ if (!globalUser) {
135
+ for (const d of det)
136
+ if (!user.some((u) => (u.cwd ?? '') === (d.cwd ?? '')))
137
+ chosen.push(d);
138
+ }
139
+ if (chosen.length === 0 && incomplete) {
140
+ const old = prev.filter((p) => p.id === id && p.available);
141
+ chosen.push(...old);
142
+ preservedStale += old.length;
143
+ }
144
+ if (chosen.length > 0)
145
+ out.push(...chosen.sort(byCwd));
146
+ else {
147
+ // nothing confirmed, nothing detected: list the check as unavailable so the contract always names all nine checks
148
+ const unavailable = detected.find((d) => d.id === id) ?? { id, available: false, command: null, required: 'no', source: null, origin: 'inferred' };
149
+ out.push(unavailable);
150
+ }
89
151
  }
90
- return ORDER.map((id) => found.get(id) ?? { id, available: false, command: null, required: 'no', source: null, origin: 'inferred' });
152
+ return { checks: out, preservedUser, preservedStale, incomplete };
91
153
  }
92
154
  //# sourceMappingURL=commands.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/discovery/commands.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CheckId, CommandEntry } from '../config/schemas.js';\nimport type { PackageInfo } from './detect.js';\n\nexport const TRUST_NOTE =\n 'Commands marked \"repository-script\" come from files in the repository (e.g. package.json scripts) and are untrusted input. ' +\n 'Inspect what a script does before running it, especially in automation or on unfamiliar repositories.';\n\nexport interface ContractInput {\n pkg: PackageInfo | null;\n packageManager: string | null;\n ecosystems: Set<string>;\n has: (rel: string) => boolean;\n pythonEvidence: Record<string, string>;\n files: Set<string>;\n}\n\nconst SCRIPT_CANDIDATES: Record<string, string[]> = {\n typecheck: ['typecheck', 'type-check', 'check-types', 'tsc', 'types'],\n unit: ['test', 'test:unit', 'unit', 'test:run'],\n integration: ['test:integration', 'test:int', 'integration'],\n e2e: ['test:e2e', 'e2e', 'test:playwright', 'test:cypress', 'cypress:run'],\n lint: ['lint', 'eslint', 'lint:check'],\n format: ['format:check', 'prettier:check', 'format-check', 'check:format'],\n build: ['build', 'compile'],\n};\n\nconst REQUIRED: Record<CheckId, CommandEntry['required']> = {\n typecheck: 'yes',\n unit: 'yes',\n integration: 'context',\n e2e: 'context',\n lint: 'yes',\n format: 'context',\n build: 'yes',\n migrations: 'context',\n security: 'context',\n};\n\nconst ORDER: CheckId[] = ['typecheck', 'unit', 'integration', 'e2e', 'lint', 'format', 'build', 'migrations', 'security'];\n\nfunction scriptCommand(pm: string | null, script: string): string {\n const mgr = pm ?? 'npm';\n if (script === 'test' && (mgr === 'npm' || mgr === 'pnpm' || mgr === 'yarn' || mgr === 'bun')) return `${mgr} test`;\n if (mgr === 'yarn') return `yarn ${script}`;\n if (mgr === 'bun') return `bun run ${script}`;\n return `${mgr} run ${script}`;\n}\n\n/** Placeholder scripts such as npm's default \"no test specified\" do not count as tests. */\nfunction isRealScript(body: string): boolean {\n return !/no test specified/i.test(body) && body.trim() !== '';\n}\n\nexport function buildContract(input: ContractInput): CommandEntry[] {\n const found = new Map<CheckId, CommandEntry>();\n const set = (id: CheckId, command: string, source: string, origin: CommandEntry['origin']): void => {\n if (!found.has(id)) found.set(id, { id, available: true, command, required: REQUIRED[id], source, origin });\n };\n\n if (input.pkg) {\n for (const [id, names] of Object.entries(SCRIPT_CANDIDATES)) {\n for (const name of names) {\n const body = input.pkg.scripts[name];\n if (body !== undefined && isRealScript(body)) {\n set(id as CheckId, scriptCommand(input.packageManager, name), `${input.pkg.rel}#scripts.${name}`, 'repository-script');\n break;\n }\n }\n }\n if (!found.has('typecheck') && input.has('tsconfig.json') && input.pkg.hasTypes) {\n set('typecheck', `${input.packageManager === 'pnpm' ? 'pnpm exec' : 'npx'} tsc --noEmit`, 'tsconfig.json', 'inferred');\n }\n }\n\n if (input.ecosystems.has('python')) {\n const ev = input.pythonEvidence;\n const hasTestsDir = [...input.files].some((f) => /^tests?\\//.test(f) && f.endsWith('.py'));\n if (ev.pytest || hasTestsDir || input.has('pytest.ini')) set('unit', 'pytest', ev.pytest ?? 'tests/', 'inferred');\n if (ev.ruff) set('lint', 'ruff check .', ev.ruff, 'inferred');\n if (ev.mypy) set('typecheck', 'mypy .', ev.mypy, 'inferred');\n if (ev.black) set('format', 'black --check .', ev.black, 'inferred');\n if (ev.alembic) set('migrations', 'alembic check', ev.alembic, 'inferred');\n }\n if (input.ecosystems.has('go')) {\n set('unit', 'go test ./...', 'go.mod', 'inferred');\n set('build', 'go build ./...', 'go.mod', 'inferred');\n set('lint', 'go vet ./...', 'go.mod', 'inferred');\n }\n if (input.ecosystems.has('rust')) {\n set('unit', 'cargo test', 'Cargo.toml', 'inferred');\n set('build', 'cargo build', 'Cargo.toml', 'inferred');\n set('lint', 'cargo clippy', 'Cargo.toml', 'inferred');\n }\n if (input.has('pom.xml')) {\n set('unit', 'mvn test', 'pom.xml', 'inferred');\n set('build', 'mvn package', 'pom.xml', 'inferred');\n }\n if (input.has('build.gradle') || input.has('build.gradle.kts')) {\n set('unit', 'gradle test', 'build.gradle', 'inferred');\n set('build', 'gradle build', 'build.gradle', 'inferred');\n }\n\n return ORDER.map(\n (id) => found.get(id) ?? { id, available: false, command: null, required: 'no' as const, source: null, origin: 'inferred' as const },\n );\n}\n"]}
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/discovery/commands.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CheckId, CommandEntry } from '../config/schemas.js';\nimport type { PackageInfo } from './detect.js';\n\nexport const TRUST_NOTE =\n 'Commands marked \"repository-script\" come from files in the repository (e.g. package.json scripts) and are untrusted input. ' +\n 'Inspect what a script does before running it, especially in automation or on unfamiliar repositories.';\n\n/** One Node.js package: the manifest, where it lives, and which package manager its own lockfile says it uses. */\nexport interface NodePackageInput {\n /** Directory relative to the project root, POSIX separators; '' for the root. */\n dir: string;\n pkg: PackageInfo;\n packageManager: string;\n hasTsconfig: boolean;\n}\n\nexport interface ContractInput {\n nodePackages: NodePackageInput[];\n ecosystems: Set<string>;\n has: (rel: string) => boolean;\n pythonEvidence: Record<string, string>;\n files: Set<string>;\n}\n\n/** First match wins, so more specific scripts (test:unit) are preferred over catch-alls (test). */\nconst SCRIPT_CANDIDATES: Record<string, string[]> = {\n typecheck: ['typecheck', 'type-check', 'check-types', 'tsc', 'types'],\n unit: ['test:unit', 'unit', 'test', 'test:run'],\n integration: ['test:integration', 'test:int', 'integration'],\n e2e: ['test:e2e', 'e2e', 'test:playwright', 'test:cypress', 'cypress:run'],\n lint: ['lint', 'eslint', 'lint:check'],\n format: ['format:check', 'prettier:check', 'format-check', 'check:format'],\n build: ['build', 'compile'],\n migrations: ['db:migrate', 'migrate'],\n security: ['test:security', 'security:test', 'security', 'audit:security'],\n};\n\nconst REQUIRED: Record<CheckId, CommandEntry['required']> = {\n typecheck: 'yes',\n unit: 'yes',\n integration: 'context',\n e2e: 'context',\n lint: 'yes',\n format: 'context',\n build: 'yes',\n migrations: 'context',\n security: 'context',\n};\n\nexport const CHECK_ORDER: CheckId[] = ['typecheck', 'unit', 'integration', 'e2e', 'lint', 'format', 'build', 'migrations', 'security'];\n\nfunction scriptCommand(pm: string, script: string): string {\n if (script === 'test' && (pm === 'npm' || pm === 'pnpm' || pm === 'yarn' || pm === 'bun')) return `${pm} test`;\n if (pm === 'yarn') return `yarn ${script}`;\n if (pm === 'bun') return `bun run ${script}`;\n return `${pm} run ${script}`;\n}\n\n/** Placeholder scripts such as npm's default \"no test specified\" do not count as tests. */\nfunction isRealScript(body: string): boolean {\n return !/no test specified/i.test(body) && body.trim() !== '';\n}\n\nconst byCwd = (a: CommandEntry, b: CommandEntry): number => (a.cwd ?? '').localeCompare(b.cwd ?? '');\n\n/**\n * Detected verification commands. Node commands are produced per package and only for scripts that exist in that\n * package's own manifest. Every check that no package provides gets one unavailable entry so the contract always\n * lists all nine checks.\n */\nexport function buildContract(input: ContractInput): CommandEntry[] {\n const found: CommandEntry[] = [];\n const add = (id: CheckId, command: string, source: string, origin: CommandEntry['origin'], cwd?: string): void => {\n if (!found.some((f) => f.id === id && (f.cwd ?? '') === (cwd ?? ''))) {\n found.push({ id, available: true, command, required: REQUIRED[id], source, origin, ...(cwd ? { cwd } : {}) });\n }\n };\n\n for (const np of input.nodePackages) {\n const manifest = np.dir ? `${np.dir}/package.json` : 'package.json';\n for (const [id, names] of Object.entries(SCRIPT_CANDIDATES)) {\n for (const name of names) {\n const body = np.pkg.scripts[name];\n if (body !== undefined && isRealScript(body)) {\n add(id as CheckId, scriptCommand(np.packageManager, name), `${manifest}#scripts.${name}`, 'repository-script', np.dir);\n break;\n }\n }\n }\n if (np.pkg.scripts.typecheck === undefined && np.hasTsconfig && np.pkg.hasTypes && !found.some((f) => f.id === 'typecheck' && (f.cwd ?? '') === np.dir)) {\n add('typecheck', `${np.packageManager === 'pnpm' ? 'pnpm exec' : 'npx'} tsc --noEmit`, `${np.dir ? `${np.dir}/` : ''}tsconfig.json`, 'inferred', np.dir);\n }\n }\n\n if (input.ecosystems.has('python')) {\n const ev = input.pythonEvidence;\n const hasTestsDir = [...input.files].some((f) => /^tests?\\//.test(f) && f.endsWith('.py'));\n if (ev.pytest || hasTestsDir || input.has('pytest.ini')) add('unit', 'pytest', ev.pytest ?? 'tests/', 'inferred');\n if (ev.ruff) add('lint', 'ruff check .', ev.ruff, 'inferred');\n if (ev.mypy) add('typecheck', 'mypy .', ev.mypy, 'inferred');\n if (ev.black) add('format', 'black --check .', ev.black, 'inferred');\n if (ev.alembic) add('migrations', 'alembic check', ev.alembic, 'inferred');\n }\n if (input.ecosystems.has('go')) {\n add('unit', 'go test ./...', 'go.mod', 'inferred');\n add('build', 'go build ./...', 'go.mod', 'inferred');\n add('lint', 'go vet ./...', 'go.mod', 'inferred');\n }\n if (input.ecosystems.has('rust')) {\n add('unit', 'cargo test', 'Cargo.toml', 'inferred');\n add('build', 'cargo build', 'Cargo.toml', 'inferred');\n add('lint', 'cargo clippy', 'Cargo.toml', 'inferred');\n }\n if (input.has('pom.xml')) {\n add('unit', 'mvn test', 'pom.xml', 'inferred');\n add('build', 'mvn package', 'pom.xml', 'inferred');\n }\n if (input.has('build.gradle') || input.has('build.gradle.kts')) {\n add('unit', 'gradle test', 'build.gradle', 'inferred');\n add('build', 'gradle build', 'build.gradle', 'inferred');\n }\n\n return withPlaceholders(found);\n}\n\nfunction withPlaceholders(entries: CommandEntry[]): CommandEntry[] {\n const out: CommandEntry[] = [];\n for (const id of CHECK_ORDER) {\n const mine = entries.filter((e) => e.id === id && e.available).sort(byCwd);\n if (mine.length > 0) out.push(...mine);\n else {\n const unavailable = entries.find((e) => e.id === id);\n out.push(unavailable ?? { id, available: false, command: null, required: 'no', source: null, origin: 'inferred' });\n }\n }\n return out;\n}\n\nexport interface MergeResult {\n checks: CommandEntry[];\n /** Entries kept from the previous contract because a human confirmed them (origin \"user\"). */\n preservedUser: number;\n /** Previously detected entries kept because this analysis found nothing or was cut short. */\n preservedStale: number;\n /** The new detection found no verification commands at all, or the scan was truncated. */\n incomplete: boolean;\n}\n\n/**\n * Combine the previous contract with fresh detection.\n * - Entries confirmed by a person (origin \"user\") are never dropped or overwritten. A user entry without a cwd\n * covers its whole check; one with a cwd covers only that directory, and detection fills in the other packages.\n * - If detection is incomplete, previously available entries survive too, with their original provenance.\n */\nexport function mergeContract(previous: CommandEntry[] | null, detected: CommandEntry[], opts: { truncated?: boolean } = {}): MergeResult {\n const prev = previous ?? [];\n const detectedAvailable = detected.filter((d) => d.available);\n const incomplete = detectedAvailable.length === 0 || !!opts.truncated;\n let preservedUser = 0;\n let preservedStale = 0;\n const out: CommandEntry[] = [];\n\n for (const id of CHECK_ORDER) {\n const user = prev.filter((p) => p.id === id && p.origin === 'user' && p.available);\n const globalUser = user.some((u) => !u.cwd);\n const det = detectedAvailable.filter((d) => d.id === id);\n const chosen: CommandEntry[] = [...user];\n preservedUser += user.length;\n if (!globalUser) {\n for (const d of det) if (!user.some((u) => (u.cwd ?? '') === (d.cwd ?? ''))) chosen.push(d);\n }\n if (chosen.length === 0 && incomplete) {\n const old = prev.filter((p) => p.id === id && p.available);\n chosen.push(...old);\n preservedStale += old.length;\n }\n if (chosen.length > 0) out.push(...chosen.sort(byCwd));\n else {\n // nothing confirmed, nothing detected: list the check as unavailable so the contract always names all nine checks\n const unavailable = detected.find((d) => d.id === id) ?? { id, available: false, command: null, required: 'no' as const, source: null, origin: 'inferred' as const };\n out.push(unavailable);\n }\n }\n return { checks: out, preservedUser, preservedStale, incomplete };\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import type { ScanResult } from './scan.js';
2
+ import { type NodePackageInput } from './commands.js';
2
3
  import type { CommandEntry } from '../config/schemas.js';
3
4
  export interface Detected {
4
5
  name: string;
@@ -10,6 +11,7 @@ export interface SubProject {
10
11
  name: string;
11
12
  ecosystem: string;
12
13
  frameworks: string[];
14
+ packageManager?: string;
13
15
  }
14
16
  export interface PackageInfo {
15
17
  rel: string;
@@ -49,6 +51,8 @@ export interface DiscoveryReport {
49
51
  packages: SubProject[];
50
52
  };
51
53
  commands: CommandEntry[];
54
+ /** Recognised package manifests (project-relative). Used as evidence for derived facts. */
55
+ manifests: string[];
52
56
  testConfigs: string[];
53
57
  ci: string[];
54
58
  docker: string[];
@@ -69,4 +73,17 @@ export interface DetectInput {
69
73
  projectName: string;
70
74
  scan: ScanResult;
71
75
  }
76
+ export interface FoundNodePackage extends NodePackageInput {
77
+ manifest: string;
78
+ /** Where the package manager came from, for transparency in discovery.json consumers. */
79
+ pmSource: 'lockfile' | 'packageManager field' | 'inherited from root' | 'default';
80
+ }
81
+ /**
82
+ * Find every Node.js package, not just the root one. Each package gets its own package manager from its own lockfile
83
+ * (falling back to the packageManager field, then the root lockfile, then npm). No script is ever executed.
84
+ */
85
+ export declare function discoverNodePackages(root: string, files: Set<string>): Promise<{
86
+ packages: FoundNodePackage[];
87
+ unparsable: string[];
88
+ }>;
72
89
  export declare function detectProject(input: DetectInput): Promise<DiscoveryReport>;