mustflow 2.22.15 → 2.22.17
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.
- package/dist/cli/commands/check.js +1 -0
- package/dist/cli/commands/context.js +3 -3
- package/dist/cli/commands/doctor.js +17 -5
- package/dist/cli/commands/explain.js +65 -1
- package/dist/cli/commands/index.js +2 -2
- package/dist/cli/commands/run.js +20 -4
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/commands/verify.js +16 -9
- package/dist/cli/i18n/en.js +25 -0
- package/dist/cli/i18n/es.js +25 -0
- package/dist/cli/i18n/fr.js +25 -0
- package/dist/cli/i18n/hi.js +25 -0
- package/dist/cli/i18n/ko.js +25 -0
- package/dist/cli/i18n/zh.js +25 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli/lib/filesystem.js +1 -1
- package/dist/cli/lib/git-changes.js +2 -0
- package/dist/cli/lib/local-index/index.js +1 -1
- package/dist/cli/lib/npm-version-check.js +22 -12
- package/dist/cli/lib/run-plan.js +20 -7
- package/dist/cli/lib/run-root-trust.js +33 -2
- package/dist/cli/lib/validation/test-selection.js +11 -1
- package/dist/core/active-run-locks.js +3 -1
- package/dist/core/command-intent-eligibility.js +7 -0
- package/dist/core/command-preconditions.js +27 -8
- package/dist/core/line-endings.js +2 -0
- package/dist/core/run-performance-history.js +20 -7
- package/dist/core/run-write-drift.js +12 -9
- package/dist/core/test-selection.js +13 -2
- package/dist/core/test-target-paths.js +17 -0
- package/dist/core/validation-ratchet.js +3 -1
- package/package.json +1 -1
- package/schemas/explain-report.schema.json +53 -0
- package/templates/default/i18n.toml +2 -2
- package/templates/default/locales/en/.mustflow/skills/security-privacy-review/SKILL.md +22 -7
- package/templates/default/locales/en/.mustflow/skills/security-regression-tests/SKILL.md +31 -20
- package/templates/default/manifest.toml +1 -1
package/dist/cli/i18n/hi.js
CHANGED
|
@@ -20,6 +20,10 @@ export const hiMessages = {
|
|
|
20
20
|
"value.yes": "हाँ",
|
|
21
21
|
"value.no": "नहीं",
|
|
22
22
|
"value.none": "none",
|
|
23
|
+
"value.present": "मौजूद",
|
|
24
|
+
"value.missing": "गुम",
|
|
25
|
+
"value.passed": "पास",
|
|
26
|
+
"value.failed": "फेल",
|
|
23
27
|
"command.adapters.summary": "एडाप्टर फ़ाइलें बनाए बिना होस्ट संगतता जाँचें",
|
|
24
28
|
"command.init.summary": "डिफ़ॉल्ट mustflow एजेंट वर्कफ़्लो कॉपी करें",
|
|
25
29
|
"command.check.summary": "mustflow फ़ाइलों की जाँच करें",
|
|
@@ -54,6 +58,7 @@ export const hiMessages = {
|
|
|
54
58
|
"check.help.exit.fail": "सत्यापन विफल हुआ या कमांड को अमान्य इनपुट मिला",
|
|
55
59
|
"check.result.passed": "mustflow check पास हुआ",
|
|
56
60
|
"check.result.strictPassed": "mustflow strict check पास हुआ",
|
|
61
|
+
"check.result.failed": "mustflow check फेल हुआ: {count} समस्या मिली।",
|
|
57
62
|
"contractLint.help.summary": ".mustflow/config/commands.toml में command-contract errors और warnings जाँचें.",
|
|
58
63
|
"contractLint.help.option.coverage": "change-classification reasons के लिए required_after coverage भी report करें",
|
|
59
64
|
"contractLint.help.option.suggest": "package.json, Makefile, या justfile से non-runnable intent snippets सुझाएँ",
|
|
@@ -484,6 +489,10 @@ export const hiMessages = {
|
|
|
484
489
|
"doctor.section.issueList": "समस्या सूची:",
|
|
485
490
|
"doctor.section.suggestedCommands": "सुझाई गई कमांड:",
|
|
486
491
|
"doctor.actionLabel": "चलाएँ",
|
|
492
|
+
"doctor.status.ok": "ठीक",
|
|
493
|
+
"doctor.status.warn": "चेतावनी",
|
|
494
|
+
"doctor.status.fail": "फेल",
|
|
495
|
+
"doctor.status.info": "जानकारी",
|
|
487
496
|
"doctor.diagnostic.install": "इंस्टॉल",
|
|
488
497
|
"doctor.diagnostic.validation": "सत्यापन",
|
|
489
498
|
"doctor.diagnostic.skillRoutes": "स्किल रूट",
|
|
@@ -662,6 +671,7 @@ export const hiMessages = {
|
|
|
662
671
|
"run.error.lifecycleNotOneshot": 'अस्वीकृत: कमांड "{intent}" का lifecycle = "{lifecycle}" है; mf run केवल oneshot कमांड चलाता है',
|
|
663
672
|
"run.error.runPolicy": 'mf run के लिए कमांड "{intent}" में run_policy = "agent_allowed" चाहिए',
|
|
664
673
|
"run.error.stdin": 'कमांड "{intent}" को stdin = "closed" सेट करना होगा',
|
|
674
|
+
"run.error.agentShellRequiresAllow": 'कमांड "{intent}" में mode = "shell" होने पर allow_shell = true सेट होना चाहिए',
|
|
665
675
|
"run.error.timeout": 'कमांड "{intent}" को timeout_seconds परिभाषित करना होगा',
|
|
666
676
|
"run.error.commandSource": 'कमांड "{intent}" को argv या mode = "shell" के साथ cmd परिभाषित करना होगा',
|
|
667
677
|
"run.error.unsafeIntent": 'इंटेंट "{intent}" का नाम सुरक्षित नहीं है। {detail}',
|
|
@@ -672,6 +682,8 @@ export const hiMessages = {
|
|
|
672
682
|
"run.error.blockedLongRunningCommandDetail": "argv में finite one-shot command होना चाहिए, development server, watcher, shell wrapper, interpreter loop, या background process नहीं।",
|
|
673
683
|
"run.error.cwdOutsideProject": 'कमांड "{intent}" का cwd अमान्य है: {detail}',
|
|
674
684
|
"run.error.cwdOutsideProjectDetail": "Intent cwd current root के अंदर रहना चाहिए।",
|
|
685
|
+
"run.error.invalidTestTarget": 'कमांड "{intent}" को अमान्य test target मिला। {detail}',
|
|
686
|
+
"run.error.invalidTestTargetDetail": "Test targets relative file paths होने चाहिए और '-' से शुरू नहीं हो सकते।",
|
|
675
687
|
"run.error.maxOutputBytes": 'कमांड "{intent}" में max_output_bytes अमान्य है। {detail}',
|
|
676
688
|
"run.error.maxOutputBytesDetail": "Output limit अनुमत maximum के अंदर रहनी चाहिए।",
|
|
677
689
|
"run.error.conflictingPreviewModes": "--dry-run या --plan-only में से एक इस्तेमाल करें, दोनों नहीं",
|
|
@@ -680,6 +692,9 @@ export const hiMessages = {
|
|
|
680
692
|
"run.error.timedOut": 'कमांड "{intent}" {seconds} सेकंड बाद time out हुई',
|
|
681
693
|
"run.error.outputLimitExceeded": 'कमांड "{intent}" ने max_output_bytes सीमा पार की: {message}',
|
|
682
694
|
"run.error.startFailed": 'कमांड "{intent}" शुरू नहीं हो सकी: {message}',
|
|
695
|
+
"run.error.activeLockConflict": "mf run {intent} सक्रिय रन लॉक के कारण रुका है: {detail}",
|
|
696
|
+
"run.error.activeLockConflictDetail": "{lock} सक्रिय intent {intent} से टकराता है (pid {pid})",
|
|
697
|
+
"run.error.activeLockConflictUnknown": "अज्ञात सक्रिय रन लॉक टकराव",
|
|
683
698
|
"search.help.summary": "mustflow वर्कफ़्लो के लिए स्थानीय SQLite इंडेक्स में खोजें।",
|
|
684
699
|
"search.help.option.limit": "प्रिंट किए जाने वाले परिणामों की संख्या सेट करें। डिफ़ॉल्ट: 10, अधिकतम: 50",
|
|
685
700
|
"search.help.option.scope": "इंडेक्स किए गए वर्कफ़्लो डेटा, source anchors, या दोनों चुनें। डिफ़ॉल्ट: workflow",
|
|
@@ -771,6 +786,15 @@ export const hiMessages = {
|
|
|
771
786
|
"verify.label.planSource": "Plan source",
|
|
772
787
|
"verify.label.status": "Status",
|
|
773
788
|
"verify.label.results": "Results",
|
|
789
|
+
"verify.label.completionVerdict": "Completion verdict",
|
|
790
|
+
"verify.label.matched": "Matched",
|
|
791
|
+
"verify.label.ran": "Ran",
|
|
792
|
+
"verify.label.passed": "Passed",
|
|
793
|
+
"verify.label.failed": "Failed",
|
|
794
|
+
"verify.label.skipped": "Skipped",
|
|
795
|
+
"verify.label.parallelism": "Parallelism",
|
|
796
|
+
"verify.label.parallelismNote": "Parallelism note",
|
|
797
|
+
"verify.parallelism.summary": "requested {requested}, effective {effective}, repository max {repositoryMax}, CPU available {cpuAvailable}, mode {mode}",
|
|
774
798
|
"verify.error.missingReason": "Verification reason missing है",
|
|
775
799
|
"verify.error.conflictingInputs": "--reason, --from-classification, --from-plan, या --changed में से केवल एक इस्तेमाल करें",
|
|
776
800
|
"verify.error.writePlanRequiresChanged": "--write-plan के लिए --changed चाहिए",
|
|
@@ -810,6 +834,7 @@ export const hiMessages = {
|
|
|
810
834
|
"explain.label.publicSurface": "Public surface",
|
|
811
835
|
"explain.label.validationReasons": "Validation reasons",
|
|
812
836
|
"explain.label.affectedContracts": "Affected contracts",
|
|
837
|
+
"explain.label.blockedRunPlan": "Blocked run plan",
|
|
813
838
|
"explain.error.missingTopic": "Explain topic गुम है",
|
|
814
839
|
"explain.error.missingCommand": "Command intent गुम है",
|
|
815
840
|
"explain.error.missingSkill": "Skill id गुम है",
|
package/dist/cli/i18n/ko.js
CHANGED
|
@@ -20,6 +20,10 @@ export const koMessages = {
|
|
|
20
20
|
"value.yes": "예",
|
|
21
21
|
"value.no": "아니요",
|
|
22
22
|
"value.none": "없음",
|
|
23
|
+
"value.present": "있음",
|
|
24
|
+
"value.missing": "없음",
|
|
25
|
+
"value.passed": "통과",
|
|
26
|
+
"value.failed": "실패",
|
|
23
27
|
"command.adapters.summary": "어댑터 파일을 만들지 않고 호스트 호환성을 확인합니다",
|
|
24
28
|
"command.init.summary": "기본 mustflow 에이전트 워크플로우를 복사합니다",
|
|
25
29
|
"command.check.summary": "mustflow 파일을 검사합니다",
|
|
@@ -54,6 +58,7 @@ export const koMessages = {
|
|
|
54
58
|
"check.help.exit.fail": "검증 실패 또는 잘못된 입력이 제공되었습니다",
|
|
55
59
|
"check.result.passed": "mustflow 검사 통과",
|
|
56
60
|
"check.result.strictPassed": "mustflow 엄격 검사 통과",
|
|
61
|
+
"check.result.failed": "mustflow 검사 실패: 문제 {count}개를 찾았습니다.",
|
|
57
62
|
"contractLint.help.summary": ".mustflow/config/commands.toml의 명령 계약 오류와 경고를 확인합니다.",
|
|
58
63
|
"contractLint.help.option.coverage": "변경 분류 이유에 대한 required_after 연결 상태도 보고합니다",
|
|
59
64
|
"contractLint.help.option.suggest": "package.json, Makefile, justfile에서 실행 불가 후보 조각을 제안합니다",
|
|
@@ -484,6 +489,10 @@ export const koMessages = {
|
|
|
484
489
|
"doctor.section.issueList": "문제 목록:",
|
|
485
490
|
"doctor.section.suggestedCommands": "추천 명령:",
|
|
486
491
|
"doctor.actionLabel": "명령",
|
|
492
|
+
"doctor.status.ok": "정상",
|
|
493
|
+
"doctor.status.warn": "주의",
|
|
494
|
+
"doctor.status.fail": "실패",
|
|
495
|
+
"doctor.status.info": "정보",
|
|
487
496
|
"doctor.diagnostic.install": "설치",
|
|
488
497
|
"doctor.diagnostic.validation": "검증",
|
|
489
498
|
"doctor.diagnostic.skillRoutes": "스킬 라우팅",
|
|
@@ -662,6 +671,7 @@ export const koMessages = {
|
|
|
662
671
|
"run.error.lifecycleNotOneshot": '거부됨: 명령 "{intent}"의 수명 주기(lifecycle)는 "{lifecycle}"입니다. mf run은 일회성(oneshot) 명령만 실행합니다',
|
|
663
672
|
"run.error.runPolicy": '명령 "{intent}"는 mf run에서 실행하려면 run_policy = "agent_allowed"가 필요합니다',
|
|
664
673
|
"run.error.stdin": '명령 "{intent}"는 stdin = "closed"를 설정해야 합니다',
|
|
674
|
+
"run.error.agentShellRequiresAllow": '명령 "{intent}"는 mode = "shell"일 때 allow_shell = true를 설정해야 합니다',
|
|
665
675
|
"run.error.timeout": '명령 "{intent}"는 timeout_seconds를 정의해야 합니다',
|
|
666
676
|
"run.error.commandSource": '명령 "{intent}"는 argv를 정의하거나 mode = "shell"과 cmd를 함께 정의해야 합니다',
|
|
667
677
|
"run.error.unsafeIntent": '명령 의도 "{intent}"의 이름이 안전하지 않습니다. {detail}',
|
|
@@ -672,6 +682,8 @@ export const koMessages = {
|
|
|
672
682
|
"run.error.blockedLongRunningCommandDetail": "argv는 개발 서버, 감시 명령, 셸 래퍼, 인터프리터 반복 작업, 백그라운드 프로세스가 아니라 끝나는 단발성 명령이어야 합니다.",
|
|
673
683
|
"run.error.cwdOutsideProject": '명령 "{intent}"의 실행 위치(cwd)가 올바르지 않습니다: {detail}',
|
|
674
684
|
"run.error.cwdOutsideProjectDetail": "명령 실행 위치(cwd)는 현재 루트 안에 있어야 합니다.",
|
|
685
|
+
"run.error.invalidTestTarget": '명령 "{intent}"에 올바르지 않은 테스트 대상이 전달되었습니다. {detail}',
|
|
686
|
+
"run.error.invalidTestTargetDetail": "테스트 대상은 상대 파일 경로여야 하며 '-'로 시작할 수 없습니다.",
|
|
675
687
|
"run.error.maxOutputBytes": '명령 "{intent}"의 max_output_bytes 값이 올바르지 않습니다. {detail}',
|
|
676
688
|
"run.error.maxOutputBytesDetail": "출력 상한은 허용된 최댓값 안에 있어야 합니다.",
|
|
677
689
|
"run.error.conflictingPreviewModes": "--dry-run과 --plan-only 중 하나만 사용하세요",
|
|
@@ -680,6 +692,9 @@ export const koMessages = {
|
|
|
680
692
|
"run.error.timedOut": '명령 "{intent}"가 {seconds}초 뒤 시간 초과되었습니다',
|
|
681
693
|
"run.error.outputLimitExceeded": '명령 "{intent}"가 max_output_bytes 제한을 넘었습니다: {message}',
|
|
682
694
|
"run.error.startFailed": '명령 "{intent}"를 시작하지 못했습니다: {message}',
|
|
695
|
+
"run.error.activeLockConflict": "mf run {intent} 실행이 활성 실행 잠금 때문에 차단되었습니다: {detail}",
|
|
696
|
+
"run.error.activeLockConflictDetail": "{lock} 잠금이 실행 중인 {intent} 의도와 충돌합니다(pid {pid})",
|
|
697
|
+
"run.error.activeLockConflictUnknown": "알 수 없는 활성 실행 잠금 충돌",
|
|
683
698
|
"search.help.summary": "로컬 SQLite 색인에서 mustflow 워크플로우를 검색합니다.",
|
|
684
699
|
"search.help.option.limit": "출력할 검색 결과 수를 설정합니다. 기본값: 10, 최대: 50",
|
|
685
700
|
"search.help.option.scope": "색인된 워크플로 데이터, 소스 앵커, 또는 둘 다를 선택합니다. 기본값: workflow",
|
|
@@ -771,6 +786,15 @@ export const koMessages = {
|
|
|
771
786
|
"verify.label.planSource": "계획 원본",
|
|
772
787
|
"verify.label.status": "상태",
|
|
773
788
|
"verify.label.results": "결과",
|
|
789
|
+
"verify.label.completionVerdict": "완료 판정",
|
|
790
|
+
"verify.label.matched": "일치",
|
|
791
|
+
"verify.label.ran": "실행",
|
|
792
|
+
"verify.label.passed": "통과",
|
|
793
|
+
"verify.label.failed": "실패",
|
|
794
|
+
"verify.label.skipped": "생략",
|
|
795
|
+
"verify.label.parallelism": "병렬 실행",
|
|
796
|
+
"verify.label.parallelismNote": "병렬 실행 참고",
|
|
797
|
+
"verify.parallelism.summary": "요청 {requested}, 실제 {effective}, 저장소 상한 {repositoryMax}, 사용 가능 CPU {cpuAvailable}, 모드 {mode}",
|
|
774
798
|
"verify.error.missingReason": "검증 이유가 없습니다",
|
|
775
799
|
"verify.error.conflictingInputs": "--reason, --from-classification, --from-plan, --changed 중 하나만 사용하세요",
|
|
776
800
|
"verify.error.writePlanRequiresChanged": "--write-plan에는 --changed가 필요합니다",
|
|
@@ -810,6 +834,7 @@ export const koMessages = {
|
|
|
810
834
|
"explain.label.publicSurface": "공개 표면",
|
|
811
835
|
"explain.label.validationReasons": "검증 이유",
|
|
812
836
|
"explain.label.affectedContracts": "영향받는 계약",
|
|
837
|
+
"explain.label.blockedRunPlan": "차단된 실행 계획",
|
|
813
838
|
"explain.error.missingTopic": "설명할 주제가 없습니다",
|
|
814
839
|
"explain.error.missingCommand": "설명할 명령 의도가 없습니다",
|
|
815
840
|
"explain.error.missingSkill": "설명할 스킬 식별자가 없습니다",
|
package/dist/cli/i18n/zh.js
CHANGED
|
@@ -20,6 +20,10 @@ export const zhMessages = {
|
|
|
20
20
|
"value.yes": "是",
|
|
21
21
|
"value.no": "否",
|
|
22
22
|
"value.none": "无",
|
|
23
|
+
"value.present": "存在",
|
|
24
|
+
"value.missing": "缺失",
|
|
25
|
+
"value.passed": "通过",
|
|
26
|
+
"value.failed": "失败",
|
|
23
27
|
"command.adapters.summary": "不生成适配器文件,检查宿主兼容性",
|
|
24
28
|
"command.init.summary": "复制默认的 mustflow 代理工作流",
|
|
25
29
|
"command.check.summary": "验证 mustflow 文件",
|
|
@@ -54,6 +58,7 @@ export const zhMessages = {
|
|
|
54
58
|
"check.help.exit.fail": "验证失败,或命令收到了无效输入",
|
|
55
59
|
"check.result.passed": "mustflow 检查已通过",
|
|
56
60
|
"check.result.strictPassed": "mustflow 严格检查已通过",
|
|
61
|
+
"check.result.failed": "mustflow 检查失败:发现 {count} 个问题。",
|
|
57
62
|
"contractLint.help.summary": "检查 .mustflow/config/commands.toml 中的命令契约错误和警告。",
|
|
58
63
|
"contractLint.help.option.coverage": "同时报告变更分类原因的 required_after 覆盖情况",
|
|
59
64
|
"contractLint.help.option.suggest": "从 package.json、Makefile 或 justfile 建议不可运行的 intent 片段",
|
|
@@ -484,6 +489,10 @@ export const zhMessages = {
|
|
|
484
489
|
"doctor.section.issueList": "问题列表:",
|
|
485
490
|
"doctor.section.suggestedCommands": "建议命令:",
|
|
486
491
|
"doctor.actionLabel": "运行",
|
|
492
|
+
"doctor.status.ok": "正常",
|
|
493
|
+
"doctor.status.warn": "警告",
|
|
494
|
+
"doctor.status.fail": "失败",
|
|
495
|
+
"doctor.status.info": "信息",
|
|
487
496
|
"doctor.diagnostic.install": "安装",
|
|
488
497
|
"doctor.diagnostic.validation": "验证",
|
|
489
498
|
"doctor.diagnostic.skillRoutes": "技能路由",
|
|
@@ -662,6 +671,7 @@ export const zhMessages = {
|
|
|
662
671
|
"run.error.lifecycleNotOneshot": '已拒绝:命令 "{intent}" 的 lifecycle = "{lifecycle}";mf run 只执行 oneshot 命令',
|
|
663
672
|
"run.error.runPolicy": '命令 "{intent}" 需要 run_policy = "agent_allowed" 才能通过 mf run 执行',
|
|
664
673
|
"run.error.stdin": '命令 "{intent}" 必须设置 stdin = "closed"',
|
|
674
|
+
"run.error.agentShellRequiresAllow": '当 mode = "shell" 时,命令 "{intent}" 必须设置 allow_shell = true',
|
|
665
675
|
"run.error.timeout": '命令 "{intent}" 必须定义 timeout_seconds',
|
|
666
676
|
"run.error.commandSource": '命令 "{intent}" 必须定义 argv,或定义 mode = "shell" 并提供 cmd',
|
|
667
677
|
"run.error.unsafeIntent": '意图 "{intent}" 的名称不安全。{detail}',
|
|
@@ -672,6 +682,8 @@ export const zhMessages = {
|
|
|
672
682
|
"run.error.blockedLongRunningCommandDetail": "argv 必须描述会结束的单次命令,而不是开发服务器、监听命令、shell 包装器、解释器循环或后台进程。",
|
|
673
683
|
"run.error.cwdOutsideProject": '命令 "{intent}" 的 cwd 无效:{detail}',
|
|
674
684
|
"run.error.cwdOutsideProjectDetail": "意图 cwd 必须位于当前根目录内。",
|
|
685
|
+
"run.error.invalidTestTarget": '命令 "{intent}" 收到无效测试目标。{detail}',
|
|
686
|
+
"run.error.invalidTestTargetDetail": "测试目标必须是相对文件路径,且不能以 '-' 开头。",
|
|
675
687
|
"run.error.maxOutputBytes": '命令 "{intent}" 的 max_output_bytes 无效。{detail}',
|
|
676
688
|
"run.error.maxOutputBytesDetail": "输出限制必须保持在允许的最大值内。",
|
|
677
689
|
"run.error.conflictingPreviewModes": "只能使用 --dry-run 或 --plan-only,不能同时使用",
|
|
@@ -680,6 +692,9 @@ export const zhMessages = {
|
|
|
680
692
|
"run.error.timedOut": '命令 "{intent}" 在 {seconds} 秒后超时',
|
|
681
693
|
"run.error.outputLimitExceeded": '命令 "{intent}" 超过 max_output_bytes:{message}',
|
|
682
694
|
"run.error.startFailed": '命令 "{intent}" 启动失败:{message}',
|
|
695
|
+
"run.error.activeLockConflict": "mf run {intent} 被活动运行锁阻止:{detail}",
|
|
696
|
+
"run.error.activeLockConflictDetail": "{lock} 与活动 intent {intent} 冲突(pid {pid})",
|
|
697
|
+
"run.error.activeLockConflictUnknown": "未知的活动运行锁冲突",
|
|
683
698
|
"search.help.summary": "搜索本地 SQLite 索引中的 mustflow 工作流。",
|
|
684
699
|
"search.help.option.limit": "设置要输出的结果数量。默认值:10,最大:50",
|
|
685
700
|
"search.help.option.scope": "选择已索引的工作流数据、源码 anchors,或两者。默认值:workflow",
|
|
@@ -771,6 +786,15 @@ export const zhMessages = {
|
|
|
771
786
|
"verify.label.planSource": "计划来源",
|
|
772
787
|
"verify.label.status": "状态",
|
|
773
788
|
"verify.label.results": "结果",
|
|
789
|
+
"verify.label.completionVerdict": "完成判定",
|
|
790
|
+
"verify.label.matched": "匹配",
|
|
791
|
+
"verify.label.ran": "已运行",
|
|
792
|
+
"verify.label.passed": "已通过",
|
|
793
|
+
"verify.label.failed": "已失败",
|
|
794
|
+
"verify.label.skipped": "已跳过",
|
|
795
|
+
"verify.label.parallelism": "并行度",
|
|
796
|
+
"verify.label.parallelismNote": "并行度说明",
|
|
797
|
+
"verify.parallelism.summary": "请求 {requested},实际 {effective},仓库上限 {repositoryMax},可用 CPU {cpuAvailable},模式 {mode}",
|
|
774
798
|
"verify.error.missingReason": "缺少验证原因",
|
|
775
799
|
"verify.error.conflictingInputs": "只能使用 --reason、--from-classification、--from-plan 或 --changed 其中之一",
|
|
776
800
|
"verify.error.writePlanRequiresChanged": "--write-plan 需要 --changed",
|
|
@@ -810,6 +834,7 @@ export const zhMessages = {
|
|
|
810
834
|
"explain.label.publicSurface": "公共表面",
|
|
811
835
|
"explain.label.validationReasons": "验证原因",
|
|
812
836
|
"explain.label.affectedContracts": "受影响契约",
|
|
837
|
+
"explain.label.blockedRunPlan": "被阻止的运行计划",
|
|
813
838
|
"explain.error.missingTopic": "缺少 explain 主题",
|
|
814
839
|
"explain.error.missingCommand": "缺少命令意图",
|
|
815
840
|
"explain.error.missingSkill": "缺少技能标识",
|
package/dist/cli/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
export { ensureFileTargetInsideWithoutSymlinks, ensureInside, ensureInsideWithoutSymlinks, readFileInsideWithoutSymlinks, readUtf8FileInsideWithoutSymlinks, writeFileInsideWithoutSymlinks, writeUtf8FileInsideWithoutSymlinks, } from '../../core/safe-filesystem.js';
|
|
4
4
|
import { readFileInsideWithoutSymlinks, writeFileInsideWithoutSymlinks, } from '../../core/safe-filesystem.js';
|
|
5
5
|
export function toPosixPath(value) {
|
|
6
|
-
return value.
|
|
6
|
+
return value.replace(/\\/gu, '/');
|
|
7
7
|
}
|
|
8
8
|
export function copyFileInsideWithoutSymlinks(sourceParentPath, sourcePath, targetParentPath, targetPath) {
|
|
9
9
|
const content = readFileInsideWithoutSymlinks(sourceParentPath, sourcePath);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { parseGitStatusOutput } from '../../core/change-classification.js';
|
|
3
|
+
import { createCommandEnv } from '../../core/command-env.js';
|
|
3
4
|
const GIT_STATUS_TIMEOUT_MS = 10_000;
|
|
4
5
|
const GIT_STATUS_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
5
6
|
export class GitChangedFilesError extends Error {
|
|
@@ -14,6 +15,7 @@ export function readGitChangedFiles(projectRoot) {
|
|
|
14
15
|
const result = spawnSync('git', ['status', '--porcelain=v1', '-z', '--untracked-files=all'], {
|
|
15
16
|
cwd: projectRoot,
|
|
16
17
|
encoding: 'utf8',
|
|
18
|
+
env: createCommandEnv(projectRoot, { policy: 'minimal', allowlist: [] }),
|
|
17
19
|
input: '',
|
|
18
20
|
maxBuffer: GIT_STATUS_MAX_BUFFER_BYTES,
|
|
19
21
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -2311,10 +2311,10 @@ export async function searchLocalIndex(projectRoot, query, options = {}) {
|
|
|
2311
2311
|
catch {
|
|
2312
2312
|
return searchLocalWorkflowFilesDirectly(projectRoot, databasePath, normalizedQuery, limit, scope);
|
|
2313
2313
|
}
|
|
2314
|
-
const cacheLayers = readCacheLayerSets(projectRoot);
|
|
2315
2314
|
let capabilities = searchCapabilities(false);
|
|
2316
2315
|
const results = [];
|
|
2317
2316
|
try {
|
|
2317
|
+
const cacheLayers = readCacheLayerSets(projectRoot);
|
|
2318
2318
|
const stalePaths = getStalePaths(projectRoot, database);
|
|
2319
2319
|
capabilities = readStoredSearchCapabilities(database);
|
|
2320
2320
|
const indexedMatches = getIndexedSearchMatches(database, normalizedQuery);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
const DEFAULT_NPM_REGISTRY_URL = 'https://registry.npmjs.org';
|
|
2
3
|
const DEFAULT_VERSION_CHECK_TIMEOUT_MS = 3_000;
|
|
3
4
|
const PACKAGE_MANAGER_COMMANDS = [
|
|
@@ -37,6 +38,7 @@ const PACKAGE_MANAGER_COMMANDS = [
|
|
|
37
38
|
},
|
|
38
39
|
},
|
|
39
40
|
];
|
|
41
|
+
const PACKAGE_MANAGER_IDS = PACKAGE_MANAGER_COMMANDS.map((entry) => entry.id);
|
|
40
42
|
function isRecord(value) {
|
|
41
43
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
42
44
|
}
|
|
@@ -102,23 +104,31 @@ function getTimeoutMs() {
|
|
|
102
104
|
const parsed = rawValue ? Number(rawValue) : DEFAULT_VERSION_CHECK_TIMEOUT_MS;
|
|
103
105
|
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : DEFAULT_VERSION_CHECK_TIMEOUT_MS;
|
|
104
106
|
}
|
|
105
|
-
function
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
process.execPath,
|
|
110
|
-
process.argv[1],
|
|
111
|
-
import.meta.url,
|
|
112
|
-
]
|
|
113
|
-
.filter((signal) => typeof signal === 'string' && signal.length > 0)
|
|
114
|
-
.map((signal) => signal.toLowerCase());
|
|
115
|
-
for (const id of ['bun', 'pnpm', 'yarn', 'deno', 'npm']) {
|
|
116
|
-
if (signals.some((signal) => signal.includes(id))) {
|
|
107
|
+
function packageManagerFromUserAgent(userAgent) {
|
|
108
|
+
const firstToken = userAgent?.trim().toLowerCase().split(/\s+/u)[0] ?? '';
|
|
109
|
+
for (const id of PACKAGE_MANAGER_IDS) {
|
|
110
|
+
if (firstToken === id || firstToken.startsWith(`${id}/`)) {
|
|
117
111
|
return id;
|
|
118
112
|
}
|
|
119
113
|
}
|
|
120
114
|
return null;
|
|
121
115
|
}
|
|
116
|
+
function packageManagerFromExecutablePath(executablePath) {
|
|
117
|
+
if (!executablePath) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const normalized = path
|
|
121
|
+
.basename(executablePath)
|
|
122
|
+
.toLowerCase()
|
|
123
|
+
.replace(/\.(?:cmd|ps1|exe|cjs|mjs|js)$/u, '')
|
|
124
|
+
.replace(/-cli$/u, '');
|
|
125
|
+
return PACKAGE_MANAGER_IDS.find((id) => normalized === id) ?? null;
|
|
126
|
+
}
|
|
127
|
+
function detectPackageManagerId() {
|
|
128
|
+
return (packageManagerFromUserAgent(process.env.npm_config_user_agent) ??
|
|
129
|
+
packageManagerFromExecutablePath(process.env.npm_execpath) ??
|
|
130
|
+
packageManagerFromExecutablePath(process.execPath));
|
|
131
|
+
}
|
|
122
132
|
function getPackageInstallCommands(packageName) {
|
|
123
133
|
const detectedId = detectPackageManagerId();
|
|
124
134
|
const commands = [...PACKAGE_MANAGER_COMMANDS];
|
package/dist/cli/lib/run-plan.js
CHANGED
|
@@ -7,6 +7,7 @@ import { inspectActiveRunLocks, } from '../../core/active-run-locks.js';
|
|
|
7
7
|
import { isRecord, readPositiveInteger, readString, readStringArray, } from '../../core/config-loading.js';
|
|
8
8
|
import { DEFAULT_COMMAND_MAX_OUTPUT_BYTES, COMMAND_OUTPUT_LIMIT_SCOPE, MAX_COMMAND_OUTPUT_BYTES, commandMaxOutputBytesLimitMessage, } from '../../core/command-output-limits.js';
|
|
9
9
|
import { normalizeSuccessExitCodes } from '../../core/success-exit-codes.js';
|
|
10
|
+
import { normalizeSafeTestTargetPath, TEST_TARGET_PATH_ERROR } from '../../core/test-target-paths.js';
|
|
10
11
|
import { evaluateCommandPreconditions, } from '../../core/command-preconditions.js';
|
|
11
12
|
import { t } from './i18n.js';
|
|
12
13
|
function getSuccessExitCodes(intent) {
|
|
@@ -28,12 +29,18 @@ function getRelativeProjectPath(projectRoot, targetPath) {
|
|
|
28
29
|
return relativePath.length > 0 ? toPosixPath(relativePath) : '.';
|
|
29
30
|
}
|
|
30
31
|
function normalizeTestTargets(values) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
const normalizedValues = [];
|
|
33
|
+
for (const value of values ?? []) {
|
|
34
|
+
const normalized = normalizeSafeTestTargetPath(value);
|
|
35
|
+
if (normalized === null) {
|
|
36
|
+
return { ok: false, detail: `Test target ${JSON.stringify(value)} is invalid: ${TEST_TARGET_PATH_ERROR}.` };
|
|
37
|
+
}
|
|
38
|
+
normalizedValues.push(normalized);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
ok: true,
|
|
42
|
+
values: [...new Set(normalizedValues)].sort((left, right) => left.localeCompare(right)),
|
|
43
|
+
};
|
|
37
44
|
}
|
|
38
45
|
function commandAcceptsTestTargets(intent) {
|
|
39
46
|
return isRecord(intent.selection) && intent.selection.accepts_test_targets === true;
|
|
@@ -190,7 +197,13 @@ export function createRunPlan(projectRoot, contract, intentName, options = {}) {
|
|
|
190
197
|
catch (error) {
|
|
191
198
|
return createBlockedRunPlan(contract, intentName, rawIntent, eligibility, 'cwd_outside_project', error instanceof Error ? error.message : String(error), preconditions);
|
|
192
199
|
}
|
|
193
|
-
const
|
|
200
|
+
const normalizedTestTargets = commandAcceptsTestTargets(rawIntent) ?
|
|
201
|
+
normalizeTestTargets(options.testTargets) :
|
|
202
|
+
{ ok: true, values: [] };
|
|
203
|
+
if (!normalizedTestTargets.ok) {
|
|
204
|
+
return createBlockedRunPlan(contract, intentName, rawIntent, eligibility, 'invalid_test_target', normalizedTestTargets.detail, preconditions);
|
|
205
|
+
}
|
|
206
|
+
const testTargets = normalizedTestTargets.values;
|
|
194
207
|
const commandArgv = metadata.commandArgv && testTargets.length > 0 ? [...metadata.commandArgv, ...testTargets] : metadata.commandArgv;
|
|
195
208
|
if (!metadata.timeoutSeconds || !metadata.mode) {
|
|
196
209
|
return createBlockedRunPlan(contract, intentName, rawIntent, eligibility, !metadata.timeoutSeconds ? 'missing_timeout' : 'missing_command_source', !metadata.timeoutSeconds ? 'Intent timeout_seconds is missing or invalid.' : 'Intent does not define argv or shell cmd.', preconditions);
|
|
@@ -1,8 +1,39 @@
|
|
|
1
|
-
import { MANIFEST_LOCK_RELATIVE_PATH,
|
|
1
|
+
import { MANIFEST_LOCK_RELATIVE_PATH, inspectManifestLock } from './manifest-lock.js';
|
|
2
2
|
export const ALLOW_UNTRUSTED_ROOT_OPTION = '--allow-untrusted-root';
|
|
3
|
+
const REQUIRED_RUN_TRUST_LOCK_PATHS = [
|
|
4
|
+
'AGENTS.md',
|
|
5
|
+
'.mustflow/config/commands.toml',
|
|
6
|
+
];
|
|
3
7
|
export function assessRunRootTrust(projectRoot) {
|
|
4
|
-
const
|
|
8
|
+
const inspection = inspectManifestLock(projectRoot);
|
|
9
|
+
const { readResult } = inspection;
|
|
5
10
|
if (readResult.kind === 'present') {
|
|
11
|
+
if (readResult.lock.files.length === 0) {
|
|
12
|
+
return {
|
|
13
|
+
trusted: false,
|
|
14
|
+
reason: 'manifest_lock_invalid',
|
|
15
|
+
manifestLockPath: readResult.lockPath,
|
|
16
|
+
detail: 'Manifest lock must track at least one file.',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const trackedPaths = new Set(readResult.lock.files.map((file) => file.relativePath));
|
|
20
|
+
const missingRequiredPath = REQUIRED_RUN_TRUST_LOCK_PATHS.find((relativePath) => !trackedPaths.has(relativePath));
|
|
21
|
+
if (missingRequiredPath) {
|
|
22
|
+
return {
|
|
23
|
+
trusted: false,
|
|
24
|
+
reason: 'manifest_lock_invalid',
|
|
25
|
+
manifestLockPath: readResult.lockPath,
|
|
26
|
+
detail: `Manifest lock must track ${missingRequiredPath}.`,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (inspection.issues.length > 0) {
|
|
30
|
+
return {
|
|
31
|
+
trusted: false,
|
|
32
|
+
reason: 'manifest_lock_invalid',
|
|
33
|
+
manifestLockPath: readResult.lockPath,
|
|
34
|
+
detail: inspection.issues[0] ?? 'Manifest lock does not match the current workflow files.',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
6
37
|
return {
|
|
7
38
|
trusted: true,
|
|
8
39
|
reason: 'manifest_lock_present',
|
|
@@ -2,9 +2,19 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { isRecord } from '../command-contract.js';
|
|
4
4
|
import { readMustflowTomlFile } from '../toml.js';
|
|
5
|
+
import { normalizeSafeTestTargetPath, TEST_TARGET_PATH_ERROR } from '../../../core/test-target-paths.js';
|
|
5
6
|
import { ALLOWED_TEST_SELECTION_RISKS, FORBIDDEN_TEST_SELECTION_COMMAND_AUTHORITY_FIELDS, TEST_SELECTION_CONFIG_PATH, } from './constants.js';
|
|
6
7
|
import { isConfiguredCommandIntent, isDeclaredCommandIntent } from './command-intents.js';
|
|
7
8
|
import { hasOwn, pushStrictIssue, validateAllowedStringField, validateNestedTable, validatePathArrayField, validateRequiredStringField, validateStringArrayField, } from './primitives.js';
|
|
9
|
+
function validateTestTargetPathArrayField(table, key, label, issues) {
|
|
10
|
+
if (!hasOwn(table, key)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const value = table[key];
|
|
14
|
+
if (!Array.isArray(value) || value.length === 0 || !value.every((entry) => normalizeSafeTestTargetPath(entry) !== null)) {
|
|
15
|
+
issues.push({ message: `${label} ${TEST_TARGET_PATH_ERROR}` });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
8
18
|
function validateNoTestSelectionCommandAuthorityFields(label, table, issues) {
|
|
9
19
|
for (const field of FORBIDDEN_TEST_SELECTION_COMMAND_AUTHORITY_FIELDS) {
|
|
10
20
|
if (hasOwn(table, field)) {
|
|
@@ -59,7 +69,7 @@ function validateTestSelectionRule(rule, index, commandsToml, issues) {
|
|
|
59
69
|
validateNoTestSelectionCommandAuthorityFields(`${label}.select`, select, issues);
|
|
60
70
|
validateTestSelectionIntentReference(select.intent, `${label}.select.intent`, commandsToml, issues);
|
|
61
71
|
validateTestSelectionIntentReference(select.fallback_intent, `${label}.select.fallback_intent`, commandsToml, issues);
|
|
62
|
-
|
|
72
|
+
validateTestTargetPathArrayField(select, 'test_targets', `${TEST_SELECTION_CONFIG_PATH} ${label}.select.test_targets`, issues);
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
export function validateStrictTestSelectionConfig(projectRoot, commandsToml, issues) {
|
|
@@ -195,7 +195,7 @@ function acquireMutex(projectRoot) {
|
|
|
195
195
|
const root = activeLockRoot(projectRoot);
|
|
196
196
|
const mutex = activeLockMutexDirectory(projectRoot);
|
|
197
197
|
mkdirSync(root, { recursive: true });
|
|
198
|
-
|
|
198
|
+
let startedAt = Date.now();
|
|
199
199
|
while (true) {
|
|
200
200
|
try {
|
|
201
201
|
mkdirSync(mutex);
|
|
@@ -215,11 +215,13 @@ function acquireMutex(projectRoot) {
|
|
|
215
215
|
const staleByAge = Number.isFinite(ownerStartedAt) && Date.now() - ownerStartedAt > LOCK_MUTEX_STALE_MS;
|
|
216
216
|
if (!isProcessLive(ownerPid) || staleByAge) {
|
|
217
217
|
rmSync(mutex, { recursive: true, force: true });
|
|
218
|
+
startedAt = Date.now();
|
|
218
219
|
continue;
|
|
219
220
|
}
|
|
220
221
|
}
|
|
221
222
|
catch {
|
|
222
223
|
rmSync(mutex, { recursive: true, force: true });
|
|
224
|
+
startedAt = Date.now();
|
|
223
225
|
continue;
|
|
224
226
|
}
|
|
225
227
|
throw new Error('active_run_lock_mutex_busy');
|
|
@@ -76,6 +76,13 @@ export function evaluateCommandIntentEligibility(intentName, rawIntent) {
|
|
|
76
76
|
detail: blockedPattern.detail,
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
|
+
if (rawIntent.mode === 'shell' && rawIntent.allow_shell !== true) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
code: 'agent_shell_requires_allow',
|
|
83
|
+
detail: `Agent-runnable shell intent ${intentName} must set allow_shell = true.`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
79
86
|
return {
|
|
80
87
|
ok: true,
|
|
81
88
|
code: 'ok',
|
|
@@ -108,13 +108,14 @@ function listProjectFiles(projectRoot) {
|
|
|
108
108
|
walk(projectRoot);
|
|
109
109
|
return files.sort((left, right) => left.localeCompare(right));
|
|
110
110
|
}
|
|
111
|
-
function matchingSourceFiles(projectRoot, patterns) {
|
|
111
|
+
function matchingSourceFiles(projectRoot, patterns, context) {
|
|
112
112
|
const safePatterns = patterns.filter((pattern) => !relativePathIsUnsafe(pattern));
|
|
113
113
|
const matchers = safePatterns.map(globToRegExp);
|
|
114
114
|
if (matchers.length === 0) {
|
|
115
115
|
return [];
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
context.projectFiles ??= listProjectFiles(projectRoot);
|
|
118
|
+
return context.projectFiles.filter((filePath) => matchers.some((matcher) => matcher.test(filePath)));
|
|
118
119
|
}
|
|
119
120
|
function evaluatePathExists(projectRoot, declaration, satisfyIntent) {
|
|
120
121
|
const pathValue = declaration.path;
|
|
@@ -158,7 +159,7 @@ function evaluatePathExists(projectRoot, declaration, satisfyIntent) {
|
|
|
158
159
|
satisfyIntent,
|
|
159
160
|
};
|
|
160
161
|
}
|
|
161
|
-
function evaluateArtifactFreshness(projectRoot, declaration, satisfyIntent) {
|
|
162
|
+
function evaluateArtifactFreshness(projectRoot, declaration, satisfyIntent, context) {
|
|
162
163
|
const artifact = declaration.artifact;
|
|
163
164
|
if (!artifact || declaration.sources.length === 0) {
|
|
164
165
|
return {
|
|
@@ -200,7 +201,7 @@ function evaluateArtifactFreshness(projectRoot, declaration, satisfyIntent) {
|
|
|
200
201
|
satisfyIntent,
|
|
201
202
|
};
|
|
202
203
|
}
|
|
203
|
-
const sourceFiles = matchingSourceFiles(projectRoot, declaration.sources);
|
|
204
|
+
const sourceFiles = matchingSourceFiles(projectRoot, declaration.sources, context);
|
|
204
205
|
if (sourceFiles.length === 0) {
|
|
205
206
|
return {
|
|
206
207
|
kind: declaration.kind,
|
|
@@ -215,9 +216,26 @@ function evaluateArtifactFreshness(projectRoot, declaration, satisfyIntent) {
|
|
|
215
216
|
};
|
|
216
217
|
}
|
|
217
218
|
const artifactMtime = statSync(artifactPath).mtimeMs;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
let newest = null;
|
|
220
|
+
for (const source of sourceFiles) {
|
|
221
|
+
const mtime = statSync(path.join(projectRoot, ...source.split('/'))).mtimeMs;
|
|
222
|
+
if (!newest || mtime > newest.mtime) {
|
|
223
|
+
newest = { source, mtime };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (!newest) {
|
|
227
|
+
return {
|
|
228
|
+
kind: declaration.kind,
|
|
229
|
+
label: declaration.label,
|
|
230
|
+
status: 'unknown',
|
|
231
|
+
detail: 'no readable source files matched the freshness precondition.',
|
|
232
|
+
path: null,
|
|
233
|
+
artifact,
|
|
234
|
+
sources: declaration.sources,
|
|
235
|
+
newestSource: null,
|
|
236
|
+
satisfyIntent,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
221
239
|
const stale = newest.mtime > artifactMtime;
|
|
222
240
|
return {
|
|
223
241
|
kind: declaration.kind,
|
|
@@ -238,13 +256,14 @@ export function evaluateCommandPreconditions(projectRoot, contract, intentName)
|
|
|
238
256
|
if (!isRecord(intent)) {
|
|
239
257
|
return [];
|
|
240
258
|
}
|
|
259
|
+
const context = { projectFiles: null };
|
|
241
260
|
return readPreconditionDeclarations(intent).map((declaration) => {
|
|
242
261
|
const satisfyIntent = createSatisfyIntentSummary(contract, declaration.satisfyIntent);
|
|
243
262
|
if (declaration.kind === 'path_exists') {
|
|
244
263
|
return evaluatePathExists(projectRoot, declaration, satisfyIntent);
|
|
245
264
|
}
|
|
246
265
|
if (declaration.kind === 'artifact_freshness') {
|
|
247
|
-
return evaluateArtifactFreshness(projectRoot, declaration, satisfyIntent);
|
|
266
|
+
return evaluateArtifactFreshness(projectRoot, declaration, satisfyIntent, context);
|
|
248
267
|
}
|
|
249
268
|
return {
|
|
250
269
|
kind: declaration.kind,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { createCommandEnv } from './command-env.js';
|
|
4
5
|
import { readFileInsideWithoutSymlinks, writeFileInsideWithoutSymlinks } from './safe-filesystem.js';
|
|
5
6
|
const GITATTRIBUTES_PATH = '.gitattributes';
|
|
6
7
|
function toPosixPath(value) {
|
|
@@ -18,6 +19,7 @@ function gitList(projectRoot, args) {
|
|
|
18
19
|
const result = spawnSync('git', [...args, '-z'], {
|
|
19
20
|
cwd: projectRoot,
|
|
20
21
|
encoding: 'buffer',
|
|
22
|
+
env: createCommandEnv(projectRoot, { policy: 'minimal', allowlist: [] }),
|
|
21
23
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
22
24
|
windowsHide: true,
|
|
23
25
|
});
|