@wrongstack/tools 0.296.4 → 0.298.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.
- package/dist/_spawn-stream.d.ts +2 -0
- package/dist/_util.d.ts +29 -1
- package/dist/audit.js +128 -65
- package/dist/bash.js +20 -4
- package/dist/batch-tool-use.js +70 -7
- package/dist/browser/index.js +31 -1
- package/dist/builtin.js +883 -351
- package/dist/codebase-index/index.js +95 -16
- package/dist/codebase-index/project-server-protocol.d.ts +26 -0
- package/dist/codebase-index/project-server.js +76 -16
- package/dist/codebase-index/worker.js +36 -13
- package/dist/codebase-index/writer-search-helpers.d.ts +16 -0
- package/dist/codebase-index/writer.d.ts +13 -1
- package/dist/design.d.ts +1 -1
- package/dist/design.js +4 -3
- package/dist/diff.js +11 -1
- package/dist/e2e.js +9 -6
- package/dist/edit.js +9 -6
- package/dist/exec.js +562 -516
- package/dist/format.js +131 -65
- package/dist/git.js +22 -2
- package/dist/glob.js +11 -5
- package/dist/grep.js +160 -104
- package/dist/index.js +834 -290
- package/dist/install.js +135 -68
- package/dist/json.js +64 -18
- package/dist/kanban-board-inputs.d.ts +21 -4
- package/dist/kanban.js +20 -5
- package/dist/languages/index.js +143 -71
- package/dist/lint.js +128 -65
- package/dist/next-steps.d.ts +23 -0
- package/dist/next-steps.js +4 -0
- package/dist/outdated.js +101 -26
- package/dist/pack.js +883 -351
- package/dist/patch.js +11 -1
- package/dist/plan.js +32 -10
- package/dist/process-registry.d.ts +9 -0
- package/dist/process-registry.js +15 -0
- package/dist/ps-slash.js +15 -0
- package/dist/read.js +104 -23
- package/dist/replace.js +11 -3
- package/dist/scaffold.js +9 -6
- package/dist/session-kanban.js +21 -5
- package/dist/task.js +3 -1
- package/dist/test.js +128 -65
- package/dist/todo.js +3 -1
- package/dist/tool-tier.js +883 -351
- package/dist/tool-use.d.ts +1 -1
- package/dist/tool-use.js +28 -4
- package/dist/tree.d.ts +1 -0
- package/dist/tree.js +44 -8
- package/dist/typecheck.js +128 -65
- package/dist/write.js +9 -6
- package/package.json +5 -4
- package/dist/_concurrency.d.ts.map +0 -1
- package/dist/_danger-detect.d.ts.map +0 -1
- package/dist/_edit-match.d.ts.map +0 -1
- package/dist/_env.d.ts.map +0 -1
- package/dist/_fetch-guard.d.ts.map +0 -1
- package/dist/_output-spool.d.ts.map +0 -1
- package/dist/_redact-command.d.ts.map +0 -1
- package/dist/_regex.d.ts.map +0 -1
- package/dist/_session-shell.d.ts.map +0 -1
- package/dist/_shell-pick.d.ts.map +0 -1
- package/dist/_spawn-stream.d.ts.map +0 -1
- package/dist/_syntax-check.d.ts.map +0 -1
- package/dist/_util.d.ts.map +0 -1
- package/dist/_win32-resolve.d.ts.map +0 -1
- package/dist/audit.d.ts.map +0 -1
- package/dist/audit.js.map +0 -7
- package/dist/auto-proceed-loop-guard.d.ts.map +0 -1
- package/dist/auto-proceed-loop-guard.js.map +0 -7
- package/dist/bash-kill-guard.d.ts.map +0 -1
- package/dist/bash.d.ts.map +0 -1
- package/dist/bash.js.map +0 -7
- package/dist/batch-tool-use.d.ts.map +0 -1
- package/dist/batch-tool-use.js.map +0 -7
- package/dist/browser/artifacts.d.ts.map +0 -1
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js.map +0 -7
- package/dist/browser/manager.d.ts.map +0 -1
- package/dist/browser/network-guard-proxy.d.ts.map +0 -1
- package/dist/browser/security.d.ts.map +0 -1
- package/dist/browser/tools.d.ts.map +0 -1
- package/dist/browser/types.d.ts.map +0 -1
- package/dist/builtin.d.ts.map +0 -1
- package/dist/builtin.js.map +0 -7
- package/dist/circuit-breaker.d.ts.map +0 -1
- package/dist/circuit-breaker.js.map +0 -7
- package/dist/codebase-index/background-indexer.d.ts.map +0 -1
- package/dist/codebase-index/bm25.d.ts.map +0 -1
- package/dist/codebase-index/circuit-breaker.d.ts.map +0 -1
- package/dist/codebase-index/codebase-index-tool.d.ts.map +0 -1
- package/dist/codebase-index/codebase-search-tool.d.ts.map +0 -1
- package/dist/codebase-index/codebase-stats-tool.d.ts.map +0 -1
- package/dist/codebase-index/dead-code-scan.d.ts.map +0 -1
- package/dist/codebase-index/generic-parser.d.ts.map +0 -1
- package/dist/codebase-index/gitignore.d.ts.map +0 -1
- package/dist/codebase-index/go-parser.d.ts.map +0 -1
- package/dist/codebase-index/index-service.d.ts.map +0 -1
- package/dist/codebase-index/index.d.ts.map +0 -1
- package/dist/codebase-index/index.js.map +0 -7
- package/dist/codebase-index/indexer.d.ts.map +0 -1
- package/dist/codebase-index/json-parser.d.ts.map +0 -1
- package/dist/codebase-index/languages.d.ts.map +0 -1
- package/dist/codebase-index/lsp-kind.d.ts.map +0 -1
- package/dist/codebase-index/parser-dispatch.d.ts.map +0 -1
- package/dist/codebase-index/project-server-cache.d.ts.map +0 -1
- package/dist/codebase-index/project-server-client.d.ts.map +0 -1
- package/dist/codebase-index/project-server-endpoint.d.ts.map +0 -1
- package/dist/codebase-index/project-server-protocol.d.ts.map +0 -1
- package/dist/codebase-index/project-server.d.ts.map +0 -1
- package/dist/codebase-index/project-server.js.map +0 -7
- package/dist/codebase-index/py-parser.d.ts.map +0 -1
- package/dist/codebase-index/refs-extractor.d.ts.map +0 -1
- package/dist/codebase-index/rs-parser.d.ts.map +0 -1
- package/dist/codebase-index/schema.d.ts.map +0 -1
- package/dist/codebase-index/spawn-gate.d.ts.map +0 -1
- package/dist/codebase-index/sqlite-runtime.d.ts.map +0 -1
- package/dist/codebase-index/ts-parser.d.ts.map +0 -1
- package/dist/codebase-index/worker-protocol.d.ts.map +0 -1
- package/dist/codebase-index/worker.d.ts.map +0 -1
- package/dist/codebase-index/worker.js.map +0 -7
- package/dist/codebase-index/writer-admin.d.ts.map +0 -1
- package/dist/codebase-index/writer-bulk-insert.d.ts.map +0 -1
- package/dist/codebase-index/writer-graph-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-graph-reader.d.ts.map +0 -1
- package/dist/codebase-index/writer-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-pragmas.d.ts.map +0 -1
- package/dist/codebase-index/writer-ref-mapper.d.ts.map +0 -1
- package/dist/codebase-index/writer-schema.d.ts.map +0 -1
- package/dist/codebase-index/writer-search-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-store-pool.d.ts.map +0 -1
- package/dist/codebase-index/writer.d.ts.map +0 -1
- package/dist/codebase-index/yaml-parser.d.ts.map +0 -1
- package/dist/design.d.ts.map +0 -1
- package/dist/design.js.map +0 -7
- package/dist/diff.d.ts.map +0 -1
- package/dist/diff.js.map +0 -7
- package/dist/document.d.ts.map +0 -1
- package/dist/document.js.map +0 -7
- package/dist/e2e.d.ts.map +0 -1
- package/dist/e2e.js.map +0 -7
- package/dist/edit.d.ts.map +0 -1
- package/dist/edit.js.map +0 -7
- package/dist/exec-kill-guard.d.ts.map +0 -1
- package/dist/exec.d.ts.map +0 -1
- package/dist/exec.js.map +0 -7
- package/dist/fetch.d.ts.map +0 -1
- package/dist/fetch.js.map +0 -7
- package/dist/format.d.ts.map +0 -1
- package/dist/format.js.map +0 -7
- package/dist/git.d.ts.map +0 -1
- package/dist/git.js.map +0 -7
- package/dist/glob.d.ts.map +0 -1
- package/dist/glob.js.map +0 -7
- package/dist/grep.d.ts.map +0 -1
- package/dist/grep.js.map +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -7
- package/dist/install.d.ts.map +0 -1
- package/dist/install.js.map +0 -7
- package/dist/json.d.ts.map +0 -1
- package/dist/json.js.map +0 -7
- package/dist/kanban-board-inputs.d.ts.map +0 -1
- package/dist/kanban-decomposition-actions.d.ts.map +0 -1
- package/dist/kanban-detail-actions.d.ts.map +0 -1
- package/dist/kanban-evidence-bridge.d.ts.map +0 -1
- package/dist/kanban-presence.d.ts.map +0 -1
- package/dist/kanban-split-task-handler.d.ts.map +0 -1
- package/dist/kanban-task-inputs.d.ts.map +0 -1
- package/dist/kanban-tool-results.d.ts.map +0 -1
- package/dist/kanban-tool-schema.d.ts.map +0 -1
- package/dist/kanban-tool-types.d.ts.map +0 -1
- package/dist/kanban.d.ts.map +0 -1
- package/dist/kanban.js.map +0 -7
- package/dist/languages/detect.d.ts.map +0 -1
- package/dist/languages/diagnostics.d.ts.map +0 -1
- package/dist/languages/execute-tool.d.ts.map +0 -1
- package/dist/languages/execute.d.ts.map +0 -1
- package/dist/languages/index.d.ts.map +0 -1
- package/dist/languages/index.js.map +0 -7
- package/dist/languages/legacy-bridge.d.ts.map +0 -1
- package/dist/languages/package-tool.d.ts.map +0 -1
- package/dist/languages/plan.d.ts.map +0 -1
- package/dist/languages/profile-helpers.d.ts.map +0 -1
- package/dist/languages/profiles/additional.d.ts.map +0 -1
- package/dist/languages/profiles/primary.d.ts.map +0 -1
- package/dist/languages/registry.d.ts.map +0 -1
- package/dist/languages/tool.d.ts.map +0 -1
- package/dist/languages/types.d.ts.map +0 -1
- package/dist/lint.d.ts.map +0 -1
- package/dist/lint.js.map +0 -7
- package/dist/logs.d.ts.map +0 -1
- package/dist/logs.js.map +0 -7
- package/dist/memory.d.ts.map +0 -1
- package/dist/memory.js.map +0 -7
- package/dist/mode.d.ts.map +0 -1
- package/dist/mode.js.map +0 -7
- package/dist/next-steps.d.ts.map +0 -1
- package/dist/next-steps.js.map +0 -7
- package/dist/outdated.d.ts.map +0 -1
- package/dist/outdated.js.map +0 -7
- package/dist/pack.d.ts.map +0 -1
- package/dist/pack.js.map +0 -7
- package/dist/patch.d.ts.map +0 -1
- package/dist/patch.js.map +0 -7
- package/dist/plan.d.ts.map +0 -1
- package/dist/plan.js.map +0 -7
- package/dist/process-guardian.d.ts.map +0 -1
- package/dist/process-registry-persistent.d.ts.map +0 -1
- package/dist/process-registry.d.ts.map +0 -1
- package/dist/process-registry.js.map +0 -7
- package/dist/ps-slash.d.ts.map +0 -1
- package/dist/ps-slash.js.map +0 -7
- package/dist/read.d.ts.map +0 -1
- package/dist/read.js.map +0 -7
- package/dist/replace.d.ts.map +0 -1
- package/dist/replace.js.map +0 -7
- package/dist/scaffold.d.ts.map +0 -1
- package/dist/scaffold.js.map +0 -7
- package/dist/search.d.ts.map +0 -1
- package/dist/search.js.map +0 -7
- package/dist/session-kanban.d.ts.map +0 -1
- package/dist/session-kanban.js.map +0 -7
- package/dist/set-working-dir.d.ts.map +0 -1
- package/dist/skill.d.ts.map +0 -1
- package/dist/skill.js.map +0 -7
- package/dist/spawn-background.d.ts.map +0 -1
- package/dist/task.d.ts.map +0 -1
- package/dist/task.js.map +0 -7
- package/dist/test.d.ts.map +0 -1
- package/dist/test.js.map +0 -7
- package/dist/todo.d.ts.map +0 -1
- package/dist/todo.js.map +0 -7
- package/dist/tool-diff.d.ts.map +0 -1
- package/dist/tool-diff.js.map +0 -7
- package/dist/tool-help.d.ts.map +0 -1
- package/dist/tool-help.js.map +0 -7
- package/dist/tool-icon-map.d.ts.map +0 -1
- package/dist/tool-icons.d.ts.map +0 -1
- package/dist/tool-icons.js.map +0 -7
- package/dist/tool-search.d.ts.map +0 -1
- package/dist/tool-search.js.map +0 -7
- package/dist/tool-summary.d.ts.map +0 -1
- package/dist/tool-summary.js.map +0 -7
- package/dist/tool-tier.d.ts.map +0 -1
- package/dist/tool-tier.js.map +0 -7
- package/dist/tool-use.d.ts.map +0 -1
- package/dist/tool-use.js.map +0 -7
- package/dist/tree.d.ts.map +0 -1
- package/dist/tree.js.map +0 -7
- package/dist/typecheck.d.ts.map +0 -1
- package/dist/typecheck.js.map +0 -7
- package/dist/win32.d.ts.map +0 -1
- package/dist/win32.js.map +0 -7
- package/dist/write.d.ts.map +0 -1
- package/dist/write.js.map +0 -7
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-admin.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-admin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAA0B,MAAM,aAAa,CAAC;AAKhF,KAAK,SAAS,GAAG,UAAU,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC;AACrD,KAAK,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;AAEnD,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,gBAAgB,GACrB,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAIrC;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAK1E;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CA+B1F;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,gBAAgB,EACtB,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAKpB;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,MAAM,GACX,QAAQ,GAAG,IAAI,CAmBjB;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,gBAAgB,GAAG,QAAQ,EAAE,CAgB/E;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM5D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-bulk-insert.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-bulk-insert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAEvC,KAAK,SAAS,GAAG,UAAU,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC;AACrD,KAAK,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,aAAa,EAAE,GACpB,IAAI,CA6BN;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,OAAO,EACrB,IAAI,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACxC,IAAI,CAWN;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,GAAG,EAAE,GACV,IAAI,CAeN"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-graph-helpers.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-graph-helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAY,SAAS,EAAE,SAAS,EAAc,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1F;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAelE;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIxE;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAC9C,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7B;IAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAyCtE;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,EAAE,EACnC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB;IACD,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;IAC5D,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CA+BA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAC1C,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,UAAU,EAAE,MAAM,GACjB,SAAS,EAAE,CAuBb;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GACxB,MAAM,GAAG,SAAS,CAsBpB;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5B,CAAC;AAEF,wBAAgB,eAAe,CAC7B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAC7C,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,IAAI,CASN;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAC7C,QAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAC/B,SAAS,EAAE,CAoBb"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-graph-reader.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-graph-reader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,GAAG,EAAc,MAAM,aAAa,CAAC;AAgBjE,KAAK,SAAS,GAAG,UAAU,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC;AACrD,KAAK,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;AAEnD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,EAAE,CAMvF;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,EAAE,CAMzF;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY,CA4CjF;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,gBAAgB,EACtB,aAAa,EAAE,MAAM,GACpB,YAAY,CAqFd;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,GACjB,YAAY,CAsDd"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-helpers.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,aAAa,CAAC;AAE9D,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,GAAG,EAAE,CAmB9E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,GAAG,MAAM,GAAG,SAAS,CAGrB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-pragmas.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-pragmas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAe7D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-ref-mapper.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-ref-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,GAAG,CAStD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-schema.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-schema.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,0GAK9B,CAAC;AAEF,eAAO,MAAM,eAAe,unBAsB3B,CAAC;AAEF,eAAO,MAAM,gBAAgB,YAC3B,wDAAwD,EACxD,wDAAwD,EACxD,wDAAwD,EACxD,wDAAwD,EACxD,mEAAmE,EACnE,8DAA8D,EAC9D,+DAA+D,CACvD,CAAC;AAEX,eAAO,MAAM,cAAc,uNAS1B,CAAC;AAEF,eAAO,MAAM,cAAc,YACzB,wDAAwD,EACxD,uDAAuD,EACvD,2DAA2D,EAC3D,+DAA+D,CACvD,CAAC;AAEX,eAAO,MAAM,eAAe,4FAC+D,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-search-helpers.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-search-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIxE,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC9B,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIlF;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,GACtC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,GAAG,IAAI,CAiC7C;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,KAAK,SAAI,EACT,OAAO,SAAK,GACX,YAAY,CAed"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-store-pool.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer-store-pool.ts"],"names":[],"mappings":"AAAA,UAAU,gBAAgB;IACxB,KAAK,IAAI,IAAI,CAAC;CACf;AAED,KAAK,YAAY,CAAC,MAAM,SAAS,gBAAgB,IAAI,CACnD,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,KACrC,MAAM,CAAC;AAsBZ;;;;;;;;GAQG;AACH,qBAAa,SAAS,CAAC,MAAM,SAAS,gBAAgB;IAMlD,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa;IANhC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiC;IAC5D,OAAO,CAAC,KAAK,CAAK;IAElB,YACmB,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,EACjC,aAAa,GAAE,MAAgC,EAC9D;IAEJ,OAAO,CAAC,GAAG;IAIX,+DAA+D;IAC/D,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,MAAM,CAY7E;IAED;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAK3B;IAED,OAAO,CAAC,IAAI;IAkBZ,0EAA0E;IAC1E,QAAQ,IAAI,IAAI,CASf;IAED,yEAAyE;IACzE,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAWlD;IAED,+DAA+D;IAC/D,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAEnD;IAED,iDAAiD;IACjD,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/codebase-index/writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"yaml-parser.d.ts","sourceRoot":"","sources":["../../src/codebase-index/yaml-parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAyB,UAAU,EAAE,MAAM,aAAa,CAAC;AAGlF,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;CAClB,GAAG,WAAW,CASd;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/design.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"design.d.ts","sourceRoot":"","sources":["../src/design.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAUL,KAAK,YAAY,EAGlB,MAAM,yBAAyB,CAAC;AAEjC,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA+BxC,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,aAAa,GAAG,KAAK,GAAG,MAAM,GAAG,aAAa,GAAG,QAAQ,GAAG,SAAS,CAAC;IAChG,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,gGAAgG;IAChG,GAAG,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,sGAAsG;IACtG,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAChC,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,8FAA8F;IAC9F,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAC9B;AAED,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAYD;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAuTtD,CAAC"}
|
package/dist/design.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/design.ts"],
|
|
4
|
-
"sourcesContent": ["import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { DesignStack } from '@wrongstack/core/design';\nimport type { Tool } from '@wrongstack/core/types';\nimport {\n applyTokenOverrides,\n getDesignKitLoader,\n isDesignStack,\n loadActiveKit,\n materializeTokens,\n recordKitChoice,\n recordOverrides,\n resolveSemanticTune,\n runDesignVerify,\n type SemanticTune,\n setActiveKit,\n setDesignOverrides,\n} from '@wrongstack/core/design';\n\ntype Overrides = Record<string, string>;\n\n/**\n * Canonicalize `p` through `fs.realpath` so symlinks / bind mounts don't\n * hide out-of-root writes. Falls back to `path.resolve(p)` when the path\n * (or any of its parents) doesn't exist yet \u2014 callers frequently pass a\n * destination that the tool is about to write.\n */\nasync function resolveReal(p: string): Promise<string> {\n const resolved = path.resolve(p);\n let probe = resolved;\n const missing: string[] = [];\n for (;;) {\n try {\n return path.resolve(await fs.realpath(probe), ...missing);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return resolved; // reached fs root\n missing.unshift(path.basename(probe));\n probe = parent;\n continue;\n }\n // Any other error (EACCES, EBUSY, \u2026) \u2014 fall back to the lexical\n // resolve rather than throwing, so the caller can still surface a\n // meaningful error from the path.relative check below.\n return resolved;\n }\n }\n}\n\ninterface DesignInput {\n action?: 'list' | 'use' | 'foundations' | 'set' | 'tune' | 'materialize' | 'verify' | undefined;\n kit?: string | undefined;\n stack?: string | undefined;\n /** action \"set\" / \"use\": token overrides, e.g. { primary: \"oklch(...)\", \"dark.bg\": \"#111\" }. */\n set?: Overrides | undefined;\n /** action \"tune\": high-level knobs, e.g. { radius:\"lg\", density:\"compact\", font:\"Space Grotesk\" }. */\n tune?: SemanticTune | undefined;\n /** action \"materialize\": output path (project-relative); defaults per stack. */\n out?: string | undefined;\n /** action \"materialize\": overwrite an existing file. */\n force?: boolean | undefined;\n /** action \"verify\": explicit files to scan (project-relative). Defaults to a UI-file walk. */\n files?: string[] | undefined;\n}\n\ninterface DesignOutput {\n action: string;\n kit?: string | undefined;\n stack?: string | undefined;\n output: string;\n /** action \"materialize\": where the theme file was (or would be) written. */\n path?: string | undefined;\n /** action \"verify\": adherence score 0..1 and violation count. */\n score?: number | undefined;\n violations?: number | undefined;\n}\n\nfunction normalizeOverrides(set: unknown): Overrides {\n const out: Overrides = {};\n if (set && typeof set === 'object') {\n for (const [k, v] of Object.entries(set as Record<string, unknown>)) {\n if (typeof v === 'string') out[k] = v;\n }\n }\n return out;\n}\n\n/**\n * Design Studio tool \u2014 progressive disclosure + token enforcement.\n *\n * `list`/`use`/`foundations` browse and pin kits. `set` records structured color\n * overrides (these win over kit tokens). `materialize` writes the active kit's\n * (override-applied) tokens to a real, stack-appropriate theme file so the\n * palette becomes the codebase's source of truth \u2014 not just a prompt hint.\n * `verify` scans UI files for off-palette drift.\n */\nexport const designTool: Tool<DesignInput, DesignOutput> = {\n name: 'design',\n category: 'Design',\n description:\n 'Browse, load, customize, and enforce curated frontend/mobile UI design kits. Use BEFORE writing ' +\n 'UI code to commit to one coherent, modern, responsive, dark/light, accessible design. Actions: ' +\n '\"list\" (menu), \"use\" (load+pin a kit for a stack), \"foundations\" (baseline), \"set\" (override kit ' +\n 'colors/tokens), \"materialize\" (write the tokens to a real theme file \u2014 CSS @theme/OKLCH or native), ' +\n '\"verify\" (scan UI files for off-palette colors).',\n usageHint:\n 'Flow: `design {action:\"use\", kit:\"minimal-clarity\", stack:\"web\"}` \u2192 optionally ' +\n '`design {action:\"set\", set:{primary:\"oklch(62% 0.2 25)\"}}` \u2192 `design {action:\"materialize\"}` ' +\n 'to write tokens to disk \u2192 implement against them \u2192 `design {action:\"verify\"}`.',\n permission: 'confirm',\n mutating: true,\n capabilities: ['fs.write'],\n timeoutMs: 15_000,\n inputSchema: {\n type: 'object',\n properties: {\n action: {\n type: 'string',\n enum: ['list', 'use', 'foundations', 'set', 'tune', 'materialize', 'verify'],\n description:\n 'list = menu; use = load+pin a kit; foundations = baseline; set = override colors/tokens; ' +\n 'tune = high-level knobs (radius/density/font/motion); materialize = write tokens to a theme ' +\n 'file; verify = scan UI for token drift. Default: list.',\n },\n kit: {\n type: 'string',\n description: 'Kit id (required for \"use\"), e.g. \"minimal-clarity\", \"neo-brutalist\".',\n },\n stack: {\n type: 'string',\n enum: ['web', 'react-native', 'flutter', 'swiftui', 'compose'],\n description: 'Target stack \u2014 narrows guidance + materialize format. Default: web.',\n },\n set: {\n type: 'object',\n description:\n 'Token overrides for \"set\"/\"use\": { \"primary\": \"oklch(\u2026)\", \"dark.bg\": \"#111\" }. Bare key = ' +\n 'both themes; \"light.\"/\"dark.\" prefix = that theme only. Empty value clears an override.',\n additionalProperties: { type: 'string' },\n },\n tune: {\n type: 'object',\n description:\n 'High-level knobs for \"tune\": { \"radius\": \"lg\", \"density\": \"compact\", \"font\": \"Space Grotesk\", ' +\n '\"motion\": \"snappy\" }. radius = none|sm|md|lg|xl|full or a base length; density = ' +\n 'compact|cozy|comfortable; motion = snappy|smooth|none. Resolved to concrete token overrides.',\n properties: {\n radius: { type: 'string' },\n density: { type: 'string' },\n font: { type: 'string' },\n motion: { type: 'string' },\n },\n },\n out: {\n type: 'string',\n description: 'Materialize output path (project-relative). Defaults to a per-stack convention.',\n },\n force: {\n type: 'boolean',\n description: 'Materialize: overwrite an existing file (default false \u2014 refuses to clobber).',\n },\n files: {\n type: 'array',\n items: { type: 'string' },\n description: 'Verify: explicit project-relative files to scan. Default: a bounded UI-file walk.',\n },\n },\n required: [],\n },\n async execute(input, ctx): Promise<DesignOutput> {\n const loader = getDesignKitLoader(ctx.projectRoot);\n const action = input.action ?? 'list';\n const stack: DesignStack | undefined =\n input.stack && isDesignStack(input.stack) ? input.stack : undefined;\n\n if (action === 'foundations') {\n const text = await loader.foundationsText(stack);\n return { action, stack, output: text || 'No foundations document is installed.' };\n }\n\n if (action === 'use') {\n const kitId = input.kit?.trim();\n if (!kitId) {\n const menu = await loader.menuText();\n return { action, output: `No kit id provided.\\n\\n${menu}` };\n }\n const manifest = await loader.find(kitId);\n if (!manifest) {\n const menu = await loader.menuText();\n return { action, kit: kitId, output: `Kit \"${kitId}\" not found.\\n\\n${menu}` };\n }\n const resolvedStack = stack ?? manifest.stacks[0] ?? 'web';\n const body = await loader.readBody(manifest.id, resolvedStack);\n const rawTokens = await loader.readTokens(manifest.id);\n // Preserve any persisted overrides; merge in any passed with `use`.\n const persisted = await loadActiveKit(ctx.projectRoot);\n const keepOverrides =\n persisted?.kit === manifest.id ? (persisted.overrides ?? {}) : {};\n const overrides: Overrides = { ...keepOverrides, ...normalizeOverrides(input.set) };\n const tokens = rawTokens ? applyTokenOverrides(rawTokens, overrides) : rawTokens;\n\n setActiveKit(ctx, manifest.id, resolvedStack, overrides);\n await recordKitChoice(\n ctx.projectRoot,\n manifest.id,\n resolvedStack,\n 'design-tool',\n new Date().toISOString(),\n Object.keys(overrides).length ? overrides : undefined,\n );\n\n const ovLine = Object.keys(overrides).length\n ? `\\nActive color overrides: ${Object.entries(overrides)\n .map(([k, v]) => `${k}=${v}`)\n .join(', ')}\\n`\n : '';\n const header =\n `# Active design kit: ${manifest.name} (${manifest.id}) \u2014 stack: ${resolvedStack}\\n` +\n `${manifest.aesthetic}\\n${ovLine}\\n` +\n 'Implement the UI faithfully to this spec. Keep light/dark, responsive, and WCAG AA.\\n';\n const tokenBlock = tokens\n ? `\\n## Token snapshot (overrides applied)\\n\\`\\`\\`json\\n${JSON.stringify(tokens, null, 2)}\\n\\`\\`\\`\\n` +\n 'Tip: run `design {action:\"materialize\"}` to write these tokens to a real theme file.\\n'\n : '';\n return {\n action,\n kit: manifest.id,\n stack: resolvedStack,\n output: `${header}${tokenBlock}\\n${body}`,\n };\n }\n\n if (action === 'set') {\n const patch = normalizeOverrides(input.set);\n if (Object.keys(patch).length === 0) {\n return { action, output: 'No overrides given. Pass set:{ \"primary\": \"oklch(\u2026)\" }.' };\n }\n const merged = await recordOverrides(ctx.projectRoot, patch, new Date().toISOString());\n if (!merged) {\n return {\n action,\n output: 'No active kit. Pick one first: `design {action:\"use\", kit:\"<id>\"}`.',\n };\n }\n setDesignOverrides(ctx, merged);\n return {\n action,\n output:\n `Overrides updated. Active overrides: ${Object.entries(merged)\n .map(([k, v]) => `${k}=${v}`)\n .join(', ')}\\n` +\n 'These win over kit tokens. Run `design {action:\"materialize\"}` to write them to a theme file.',\n };\n }\n\n if (action === 'tune') {\n const patch = resolveSemanticTune(input.tune ?? {});\n if (Object.keys(patch).length === 0) {\n return {\n action,\n output:\n 'No recognized knobs. Pass tune:{ radius:\"lg\", density:\"compact\", font:\"\u2026\", motion:\"snappy\" }.',\n };\n }\n const merged = await recordOverrides(ctx.projectRoot, patch, new Date().toISOString());\n if (!merged) {\n return {\n action,\n output: 'No active kit. Pick one first: `design {action:\"use\", kit:\"<id>\"}`.',\n };\n }\n setDesignOverrides(ctx, merged);\n return {\n action,\n output:\n `Tuned. Resolved ${Object.keys(patch).length} token override(s): ${Object.entries(patch)\n .map(([k, v]) => `${k}=${v}`)\n .join(', ')}\\n` +\n 'Run `design {action:\"materialize\"}` to write the tuned tokens to your theme file.',\n };\n }\n\n if (action === 'materialize') {\n const active = await loadActiveKit(ctx.projectRoot);\n if (!active) {\n return {\n action,\n output: 'No active kit. Pick one first: `design {action:\"use\", kit:\"<id>\"}`.',\n };\n }\n const resolvedStack: DesignStack =\n stack ?? (active.stack && isDesignStack(active.stack) ? active.stack : 'web');\n const rawTokens = await loader.readTokens(active.kit);\n if (!rawTokens) {\n return { action, kit: active.kit, output: `Kit \"${active.kit}\" has no tokens.json.` };\n }\n const tokens = applyTokenOverrides(rawTokens, active.overrides);\n const result = materializeTokens({\n tokens,\n stack: resolvedStack,\n kitId: active.kit,\n outPath: input.out,\n });\n // Containment: result.path derives from caller-supplied input.out and\n // may be either relative (joined under projectRoot) or absolute\n // (used as-is). Pin the write inside the project root, mirroring the\n // scaffold tool.\n //\n // #249 (design.ts escape check): resolve both sides through fs.realpath\n // before the relative check, so symlinks / bind mounts (e.g. /tmp on\n // CI runners is often a symlink to /private/tmp) can't smuggle an\n // out-of-root path past the prefix-startsWith-`..` guard. The\n // destination file does not exist yet, so realpath the parent\n // directory and re-join the basename.\n //\n // Note: on Windows, path.join(absoluteA, absoluteB) concatenates\n // literally (path.join('C:\\\\A\\\\B', 'C:\\\\A\\\\C.css') === 'C:\\\\A\\\\B\\\\C:\\\\A\\\\C.css')\n // rather than collapsing absoluteB onto the drive root. Treating\n // absolute result.path as absolute (not as a join-input) keeps the\n // behavior consistent across platforms and is what the test asserts.\n const root = await resolveReal(ctx.projectRoot);\n const absResolved = path.isAbsolute(result.path)\n ? path.resolve(result.path)\n : path.resolve(path.join(ctx.projectRoot, result.path));\n const absParent = await resolveReal(path.dirname(absResolved));\n const abs = path.join(absParent, path.basename(absResolved));\n const rel = path.relative(root, abs);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(\n `design: materialize path \"${result.path}\" would escape the project root`,\n );\n }\n let exists = false;\n try {\n await fs.access(abs);\n exists = true;\n } catch {\n // does not exist \u2014 safe to write\n }\n if (exists && !input.force) {\n return {\n action,\n kit: active.kit,\n stack: resolvedStack,\n path: result.path,\n output:\n `${result.path} already exists. Re-run with force:true to overwrite, or write this ` +\n `${result.format} yourself:\\n\\n\\`\\`\\`\\n${result.content}\\n\\`\\`\\``,\n };\n }\n await fs.mkdir(path.dirname(abs), { recursive: true });\n await fs.writeFile(abs, result.content);\n return {\n action,\n kit: active.kit,\n stack: resolvedStack,\n path: result.path,\n output:\n `Wrote ${result.format} to ${result.path}. Import these tokens in your UI so the kit ` +\n `palette is the source of truth. ${exists ? '(overwrote existing file)' : ''}`,\n };\n }\n\n if (action === 'verify') {\n const active = await loadActiveKit(ctx.projectRoot);\n if (!active) {\n return {\n action,\n output: 'No active kit to verify against. Pick one: `design {action:\"use\", kit:\"<id>\"}`.',\n };\n }\n const rawTokens = await loader.readTokens(active.kit);\n if (!rawTokens) {\n return { action, kit: active.kit, output: `Kit \"${active.kit}\" has no tokens.json.` };\n }\n const tokens = applyTokenOverrides(rawTokens, active.overrides);\n const report = await runDesignVerify(ctx.projectRoot, tokens, input.files);\n const pct = Math.round(report.score * 100);\n const top = report.violations\n .slice(0, 25)\n .map((v) => ` ${v.file}:${v.line} \u2014 ${v.reason}: ${v.snippet}`)\n .join('\\n');\n const summary =\n `Adherence: ${pct}% on-palette across ${report.filesScanned} file(s). ` +\n `${report.violations.length} violation(s).` +\n (report.violations.length\n ? `\\n${top}${report.violations.length > 25 ? `\\n \u2026and ${report.violations.length - 25} more` : ''}` +\n `\\n\\nReplace off-palette colors with kit tokens (or the materialized CSS vars / token utilities).`\n : '\\nNo off-palette colors found \u2014 UI adheres to the kit palette.');\n return {\n action,\n kit: active.kit,\n output: summary,\n score: report.score,\n violations: report.violations.length,\n };\n }\n\n // Default: list\n const menu = await loader.menuText();\n return {\n action: 'list',\n output:\n (menu || 'No design kits are installed.') +\n '\\n\\nLoad one with `design {action:\"use\", kit:\"<id>\", stack:\"<stack>\"}`.',\n };\n },\n};\n"],
|
|
5
|
-
"mappings": ";AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAGtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACK;AAUP,eAAe,YAAY,GAA4B;AACrD,QAAM,WAAgB,aAAQ,CAAC;AAC/B,MAAI,QAAQ;AACZ,QAAM,UAAoB,CAAC;AAC3B,aAAS;AACP,QAAI;AACF,aAAY,aAAQ,MAAS,YAAS,KAAK,GAAG,GAAG,OAAO;AAAA,IAC1D,SAAS,KAAK;AACZ,UAAK,IAA8B,SAAS,UAAU;AACpD,cAAM,SAAc,aAAQ,KAAK;AACjC,YAAI,WAAW,MAAO,QAAO;AAC7B,gBAAQ,QAAa,cAAS,KAAK,CAAC;AACpC,gBAAQ;AACR;AAAA,MACF;AAIA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AA8BA,SAAS,mBAAmB,KAAyB;AACnD,QAAM,MAAiB,CAAC;AACxB,MAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAA8B,GAAG;AACnE,UAAI,OAAO,MAAM,SAAU,KAAI,CAAC,IAAI;AAAA,IACtC;AAAA,EACF;AACA,SAAO;AACT;AAWO,IAAM,aAA8C;AAAA,EACzD,MAAM;AAAA,EACN,UAAU;AAAA,EACV,aACE;AAAA,EAKF,WACE;AAAA,EAGF,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,cAAc,CAAC,UAAU;AAAA,EACzB,WAAW;AAAA,EACX,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,MAAM,CAAC,QAAQ,OAAO,eAAe,OAAO,QAAQ,eAAe,QAAQ;AAAA,QAC3E,aACE;AAAA,MAGJ;AAAA,MACA,KAAK;AAAA,QACH,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,CAAC,OAAO,gBAAgB,WAAW,WAAW,SAAS;AAAA,QAC7D,aAAa;AAAA,MACf;AAAA,MACA,KAAK;AAAA,QACH,MAAM;AAAA,QACN,aACE;AAAA,QAEF,sBAAsB,EAAE,MAAM,SAAS;AAAA,MACzC;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aACE;AAAA,QAGF,YAAY;AAAA,UACV,QAAQ,EAAE,MAAM,SAAS;AAAA,UACzB,SAAS,EAAE,MAAM,SAAS;AAAA,UAC1B,MAAM,EAAE,MAAM,SAAS;AAAA,UACvB,QAAQ,EAAE,MAAM,SAAS;AAAA,QAC3B;AAAA,MACF;AAAA,MACA,KAAK;AAAA,QACH,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC;AAAA,EACb;AAAA,EACA,MAAM,QAAQ,OAAO,KAA4B;AAC/C,UAAM,SAAS,mBAAmB,IAAI,WAAW;AACjD,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,QACJ,MAAM,SAAS,cAAc,MAAM,KAAK,IAAI,MAAM,QAAQ;AAE5D,QAAI,WAAW,eAAe;AAC5B,YAAM,OAAO,MAAM,OAAO,gBAAgB,KAAK;AAC/C,aAAO,EAAE,QAAQ,OAAO,QAAQ,QAAQ,wCAAwC;AAAA,IAClF;AAEA,QAAI,WAAW,OAAO;AACpB,YAAM,QAAQ,MAAM,KAAK,KAAK;AAC9B,UAAI,CAAC,OAAO;AACV,cAAMA,QAAO,MAAM,OAAO,SAAS;AACnC,eAAO,EAAE,QAAQ,QAAQ;AAAA;AAAA,EAA0BA,KAAI,GAAG;AAAA,MAC5D;AACA,YAAM,WAAW,MAAM,OAAO,KAAK,KAAK;AACxC,UAAI,CAAC,UAAU;AACb,cAAMA,QAAO,MAAM,OAAO,SAAS;AACnC,eAAO,EAAE,QAAQ,KAAK,OAAO,QAAQ,QAAQ,KAAK;AAAA;AAAA,EAAmBA,KAAI,GAAG;AAAA,MAC9E;AACA,YAAM,gBAAgB,SAAS,SAAS,OAAO,CAAC,KAAK;AACrD,YAAM,OAAO,MAAM,OAAO,SAAS,SAAS,IAAI,aAAa;AAC7D,YAAM,YAAY,MAAM,OAAO,WAAW,SAAS,EAAE;AAErD,YAAM,YAAY,MAAM,cAAc,IAAI,WAAW;AACrD,YAAM,gBACJ,WAAW,QAAQ,SAAS,KAAM,UAAU,aAAa,CAAC,IAAK,CAAC;AAClE,YAAM,YAAuB,EAAE,GAAG,eAAe,GAAG,mBAAmB,MAAM,GAAG,EAAE;AAClF,YAAM,SAAS,YAAY,oBAAoB,WAAW,SAAS,IAAI;AAEvE,mBAAa,KAAK,SAAS,IAAI,eAAe,SAAS;AACvD,YAAM;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA,QACT;AAAA,QACA;AAAA,SACA,oBAAI,KAAK,GAAE,YAAY;AAAA,QACvB,OAAO,KAAK,SAAS,EAAE,SAAS,YAAY;AAAA,MAC9C;AAEA,YAAM,SAAS,OAAO,KAAK,SAAS,EAAE,SAClC;AAAA,0BAA6B,OAAO,QAAQ,SAAS,EAClD,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,IAAI,CAAC;AAAA,IACb;AACJ,YAAM,SACJ,wBAAwB,SAAS,IAAI,KAAK,SAAS,EAAE,mBAAc,aAAa;AAAA,EAC7E,SAAS,SAAS;AAAA,EAAK,MAAM;AAAA;AAAA;AAElC,YAAM,aAAa,SACf;AAAA;AAAA;AAAA,EAAwD,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA;AAAA;AAAA,IAEvF;AACJ,aAAO;AAAA,QACL;AAAA,QACA,KAAK,SAAS;AAAA,QACd,OAAO;AAAA,QACP,QAAQ,GAAG,MAAM,GAAG,UAAU;AAAA,EAAK,IAAI;AAAA,MACzC;AAAA,IACF;AAEA,QAAI,WAAW,OAAO;AACpB,YAAM,QAAQ,mBAAmB,MAAM,GAAG;AAC1C,UAAI,OAAO,KAAK,KAAK,EAAE,WAAW,GAAG;AACnC,eAAO,EAAE,QAAQ,QAAQ,+DAA0D;AAAA,MACrF;AACA,YAAM,SAAS,MAAM,gBAAgB,IAAI,aAAa,QAAO,oBAAI,KAAK,GAAE,YAAY,CAAC;AACrF,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,MACF;AACA,yBAAmB,KAAK,MAAM;AAC9B,aAAO;AAAA,QACL;AAAA,QACA,QACE,wCAAwC,OAAO,QAAQ,MAAM,EAC1D,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,IAAI,CAAC;AAAA;AAAA,MAEjB;AAAA,IACF;AAEA,QAAI,WAAW,QAAQ;AACrB,YAAM,QAAQ,oBAAoB,MAAM,QAAQ,CAAC,CAAC;AAClD,UAAI,OAAO,KAAK,KAAK,EAAE,WAAW,GAAG;AACnC,eAAO;AAAA,UACL;AAAA,UACA,QACE;AAAA,QACJ;AAAA,MACF;AACA,YAAM,SAAS,MAAM,gBAAgB,IAAI,aAAa,QAAO,oBAAI,KAAK,GAAE,YAAY,CAAC;AACrF,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,MACF;AACA,yBAAmB,KAAK,MAAM;AAC9B,aAAO;AAAA,QACL;AAAA,QACA,QACE,mBAAmB,OAAO,KAAK,KAAK,EAAE,MAAM,uBAAuB,OAAO,QAAQ,KAAK,EACpF,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,IAAI,CAAC;AAAA;AAAA,MAEjB;AAAA,IACF;AAEA,QAAI,WAAW,eAAe;AAC5B,YAAM,SAAS,MAAM,cAAc,IAAI,WAAW;AAClD,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,MACF;AACA,YAAM,gBACJ,UAAU,OAAO,SAAS,cAAc,OAAO,KAAK,IAAI,OAAO,QAAQ;AACzE,YAAM,YAAY,MAAM,OAAO,WAAW,OAAO,GAAG;AACpD,UAAI,CAAC,WAAW;AACd,eAAO,EAAE,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ,OAAO,GAAG,wBAAwB;AAAA,MACtF;AACA,YAAM,SAAS,oBAAoB,WAAW,OAAO,SAAS;AAC9D,YAAM,SAAS,kBAAkB;AAAA,QAC/B;AAAA,QACA,OAAO;AAAA,QACP,OAAO,OAAO;AAAA,QACd,SAAS,MAAM;AAAA,MACjB,CAAC;AAkBD,YAAM,OAAO,MAAM,YAAY,IAAI,WAAW;AAC9C,YAAM,cAAmB,gBAAW,OAAO,IAAI,IACtC,aAAQ,OAAO,IAAI,IACnB,aAAa,UAAK,IAAI,aAAa,OAAO,IAAI,CAAC;AACxD,YAAM,YAAY,MAAM,YAAiB,aAAQ,WAAW,CAAC;AAC7D,YAAM,MAAW,UAAK,WAAgB,cAAS,WAAW,CAAC;AAC3D,YAAM,MAAW,cAAS,MAAM,GAAG;AACnC,UAAI,IAAI,WAAW,IAAI,KAAU,gBAAW,GAAG,GAAG;AAChD,cAAM,IAAI;AAAA,UACR,6BAA6B,OAAO,IAAI;AAAA,QAC1C;AAAA,MACF;AACA,UAAI,SAAS;AACb,UAAI;AACF,cAAS,UAAO,GAAG;AACnB,iBAAS;AAAA,MACX,QAAQ;AAAA,MAER;AACA,UAAI,UAAU,CAAC,MAAM,OAAO;AAC1B,eAAO;AAAA,UACL;AAAA,UACA,KAAK,OAAO;AAAA,UACZ,OAAO;AAAA,UACP,MAAM,OAAO;AAAA,UACb,QACE,GAAG,OAAO,IAAI,uEACX,OAAO,MAAM;AAAA;AAAA;AAAA,EAAyB,OAAO,OAAO;AAAA;AAAA,QAC3D;AAAA,MACF;AACA,YAAS,SAAW,aAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,YAAS,aAAU,KAAK,OAAO,OAAO;AACtC,aAAO;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,OAAO;AAAA,QACP,MAAM,OAAO;AAAA,QACb,QACE,SAAS,OAAO,MAAM,OAAO,OAAO,IAAI,+EACL,SAAS,8BAA8B,EAAE;AAAA,MAChF;AAAA,IACF;AAEA,QAAI,WAAW,UAAU;AACvB,YAAM,SAAS,MAAM,cAAc,IAAI,WAAW;AAClD,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,UACL;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,MACF;AACA,YAAM,YAAY,MAAM,OAAO,WAAW,OAAO,GAAG;AACpD,UAAI,CAAC,WAAW;AACd,eAAO,EAAE,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ,OAAO,GAAG,wBAAwB;AAAA,MACtF;AACA,YAAM,SAAS,oBAAoB,WAAW,OAAO,SAAS;AAC9D,YAAM,SAAS,MAAM,gBAAgB,IAAI,aAAa,QAAQ,MAAM,KAAK;AACzE,YAAM,MAAM,KAAK,MAAM,OAAO,QAAQ,GAAG;AACzC,YAAM,MAAM,OAAO,WAChB,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,MAAM,KAAK,EAAE,IAAI,IAAI,EAAE,IAAI,WAAM,EAAE,MAAM,KAAK,EAAE,OAAO,EAAE,EAC9D,KAAK,IAAI;AACZ,YAAM,UACJ,cAAc,GAAG,uBAAuB,OAAO,YAAY,aACxD,OAAO,WAAW,MAAM,oBAC1B,OAAO,WAAW,SACf;AAAA,EAAK,GAAG,GAAG,OAAO,WAAW,SAAS,KAAK;AAAA,cAAY,OAAO,WAAW,SAAS,EAAE,UAAU,EAAE;AAAA;AAAA,gGAEhG;AACN,aAAO;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,QAAQ;AAAA,QACR,OAAO,OAAO;AAAA,QACd,YAAY,OAAO,WAAW;AAAA,MAChC;AAAA,IACF;AAGA,UAAM,OAAO,MAAM,OAAO,SAAS;AACnC,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SACG,QAAQ,mCACT;AAAA,IACJ;AAAA,EACF;AACF;",
|
|
6
|
-
"names": ["menu"]
|
|
7
|
-
}
|
package/dist/diff.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAGnD,UAAU,SAAS;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;IACtC,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,IAAI,CAAC,EAAE,SAAS,GAAG,cAAc,GAAG,MAAM,GAAG,SAAS,CAAC;IACvD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CA8DhD,CAAC"}
|
package/dist/diff.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/diff.ts", "../src/_util.ts"],
|
|
4
|
-
"sourcesContent": ["import { spawn } from 'node:child_process';\nimport { statSync } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { buildChildEnv } from '@wrongstack/core/utils';\nimport type { Tool } from '@wrongstack/core/types';\nimport { safeResolve } from './_util.js';\n\ninterface DiffInput {\n path?: string | undefined;\n files?: string | string[] | undefined;\n a?: string | undefined;\n b?: string | undefined;\n staged?: boolean | undefined;\n mode?: 'unified' | 'side-by-side' | 'stat' | undefined;\n context?: number | undefined;\n}\n\ninterface DiffOutput {\n diff: string;\n files: string[];\n truncated: boolean;\n mode: string;\n}\n\nexport const diffTool: Tool<DiffInput, DiffOutput> = {\n name: 'diff',\n category: 'Filesystem',\n description:\n 'Show file content with line numbers, staged/working-tree diffs via git, or commit/branch diffs. A safer and more structured alternative to raw `git diff` via shell.',\n usageHint:\n 'USE FOR CODE REVIEW AND CHANGE INSPECTION:\\n\\n' +\n '- `files` + no `a`/`b` \u2192 show file content with line numbers (NOT a unified diff; no +/- prefixes).\\n' +\n '- `a` and/or `b` \u2192 git-style commit/branch diff (unified format, real +/- prefixes).\\n' +\n '- `staged: true` \u2192 only show staged changes.\\n' +\n '- `mode` can be \"unified\", \"stat\", or \"side-by-side\" (only affects the git-diff path).\\n' +\n '\\n' +\n 'NOTE: For a true file-vs-file unified diff, supply `a` and `b` so the tool ' +\n 'delegates to `git diff`. The `files`-only path is a line-numbered dump, not a diff.\\n' +\n '\\n' +\n 'This tool has important safety guards against flag injection (see previous security findings).',\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n icon: 'diff',\n timeoutMs: 10_000,\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Working directory for the diff operation (defaults to project root).',\n },\n files: {\n type: 'string',\n description: 'Files or globs to diff (e.g. \"src/**/*.ts\" or comma-separated list).',\n },\n a: {\n type: 'string',\n description: 'First ref/commit/branch for git diff (e.g. HEAD, main, a commit hash).',\n },\n b: {\n type: 'string',\n description: 'Second ref/commit/branch for git diff.',\n },\n staged: {\n type: 'boolean',\n description: 'If true, only show changes that are staged in git.',\n },\n mode: {\n type: 'string',\n enum: ['unified', 'side-by-side', 'stat'],\n description: 'Output format. \"unified\" is default, \"stat\" shows summary only.',\n },\n context: {\n type: 'integer',\n description: 'Number of context lines for unified diffs (default: 3).',\n },\n },\n },\n async execute(input, ctx, opts) {\n if (input.a !== undefined || input.b !== undefined) {\n return await gitDiff(input, ctx, opts.signal);\n }\n\n return await fileDiff(input, ctx, opts.signal);\n },\n};\n\nasync function gitDiff(\n input: DiffInput,\n ctx: import('@wrongstack/core/agent').Context,\n signal: AbortSignal,\n): Promise<DiffOutput> {\n // Flag injection: a/b are passed as positional args BEFORE the `--`\n // separator, so a leading '-' would be parsed as a git option. The most\n // dangerous is `--output=<path>`, which makes `git diff` write to an\n // arbitrary path (outside the project root, with no confirmation since this\n // tool is permission:'auto'). Reject leading-dash refs unconditionally \u2014\n // mirrors the guard in git.ts (validateWorktreeInput) and install.ts.\n if (input.a?.startsWith('-')) {\n throw new Error(`diff: unsafe ref \"${input.a}\" \u2014 refs may not begin with '-' (flag injection)`);\n }\n if (input.b?.startsWith('-')) {\n throw new Error(`diff: unsafe ref \"${input.b}\" \u2014 refs may not begin with '-' (flag injection)`);\n }\n\n const gitDir = findGitDir(ctx.cwd);\n if (!gitDir) {\n return { diff: '', files: [], truncated: false, mode: 'unified' };\n }\n\n const args: string[] = ['diff', '--no-color'];\n if (input.staged) args.push('--staged');\n if (input.a) args.push(input.a);\n if (input.b) args.push(input.b);\n if (input.files) {\n const files = Array.isArray(input.files) ? input.files : input.files.split(',');\n args.push('--', ...files.map((f) => f.trim()));\n }\n\n const result = await runGit(args, gitDir, signal);\n return {\n diff: result.stdout,\n files: [],\n truncated: result.stdout.length > 100_000,\n mode: 'unified',\n };\n}\n\nfunction findGitDir(cwd: string): string | null {\n let dir = cwd;\n for (let i = 0; i < 20; i++) {\n try {\n const stat = statSync(path.join(dir, '.git'));\n if (stat.isDirectory()) return dir;\n } catch {\n // continue\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nfunction runGit(\n args: string[],\n cwd: string,\n signal: AbortSignal,\n): Promise<{ stdout: string; stderr: string; exitCode: number }> {\n return new Promise((resolve) => {\n let stdout = '';\n let stderr = '';\n\n const child = spawn('git', args, {\n cwd,\n signal,\n env: buildChildEnv(),\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n });\n child.stdout?.on('data', (c) => {\n stdout += c.toString();\n });\n child.stderr?.on('data', (c) => {\n stderr += c.toString();\n });\n child.on('close', (code) => resolve({ stdout, stderr, exitCode: code ?? 0 }));\n /* v8 ignore next -- spawn 'error' only fires when the git binary is missing; defensive. */\n child.on('error', (e) => resolve({ stdout: '', stderr: e.message, exitCode: 1 }));\n });\n}\n\nasync function fileDiff(\n input: DiffInput,\n ctx: import('@wrongstack/core/agent').Context,\n _signal: AbortSignal,\n): Promise<DiffOutput> {\n // `context` is accepted on the input schema for API stability but is\n // unused in the line-dump path \u2014 there is no notion of \"context lines\"\n // when there is no real diff. The git-diff path (`a`/`b`) ignores it too.\n void input.context;\n\n const files = input.files\n ? (Array.isArray(input.files) ? input.files : input.files.split(','))\n .map((f) => f.trim())\n .filter(Boolean)\n : [];\n\n if (files.length === 0) {\n return {\n diff: 'No files specified',\n files: [],\n truncated: false,\n mode: input.mode ?? 'unified',\n };\n }\n\n const results: string[] = [];\n\n for (const file of files) {\n const absPath = safeResolve(file, ctx);\n const stat = await fs.stat(absPath).catch(() => null);\n if (!stat?.isFile()) continue;\n\n const content = await fs.readFile(absPath, 'utf8');\n const lines = content.split(/\\r?\\n/);\n results.push(formatWithLineNumbers(file, lines));\n }\n\n return {\n diff: results.join('\\n\\n'),\n files,\n truncated: false,\n mode: input.mode ?? 'unified',\n };\n}\n\n/**\n * Render a file's content as a line-numbered dump. This is intentionally\n * NOT a unified diff \u2014 it has no `-`/`+` prefixes. For a real diff\n * between two revisions, use the `a`/`b` params (delegates to `git diff`).\n *\n * Format: ` N | content` with the line number right-aligned. The\n * `context` parameter is accepted for API compatibility but unused \u2014\n * a line dump has no notion of \"context lines\".\n */\nfunction formatWithLineNumbers(file: string, lines: string[]): string {\n const width = String(lines.length).length;\n const numbered = lines.map((line, i) => `${String(i + 1).padStart(width)} | ${line}`).join('\\n');\n return `--- ${file} (line-numbered dump, not a unified diff) ---\\n${numbered}`;\n}\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core/utils';\nimport type { Context } from '@wrongstack/core/agent';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` \u2014 the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm \u2192 yarn \u2192 npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root\u2192out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink \u2014 macOS `/var`\u2192`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n\u2026[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]\u2026\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// \u2500\u2500\u2500 Command-output normalization (token-saving) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) \u2014 never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only \u2014 it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `\u2026 \u27E8repeated ${run}\u00D7\u27E9`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends \u2014 the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n\u2026[truncated ${total - kept} bytes]\u2026\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI \u2192 collapse\n * carriage-return progress \u2192 trim trailing whitespace \u2192 collapse identical\n * consecutive lines \u2192 squeeze blank-line runs \u2192 head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines \u2192 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["path", "stat", "resolve"]
|
|
7
|
-
}
|
package/dist/document.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAGnD,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;IACtC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;IAChD,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,UAAU,cAAc;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,cAAc,CAoF5D,CAAC"}
|
package/dist/document.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/document.ts", "../src/_util.ts"],
|
|
4
|
-
"sourcesContent": ["import * as fs from 'node:fs/promises';\nimport type { Tool } from '@wrongstack/core/types';\nimport { safeResolve } from './_util.js';\n\ninterface DocumentInput {\n target: 'file' | 'function' | 'class' | 'type' | 'all';\n path?: string | undefined;\n files?: string | string[] | undefined;\n style?: 'jsdoc' | 'tsdoc' | 'block' | undefined;\n overwrite?: boolean | undefined;\n cwd?: string | undefined;\n}\n\ninterface DocumentedItem {\n path: string;\n name: string;\n signature: string;\n docstring: string;\n status: 'documented' | 'skipped' | 'error';\n error?: string | undefined;\n}\n\ninterface DocumentOutput {\n files_processed: number;\n items_documented: number;\n results: DocumentedItem[];\n style: string;\n}\n\nexport const documentTool: Tool<DocumentInput, DocumentOutput> = {\n name: 'document',\n category: 'Project',\n description:\n 'DEPRECATED \u2014 use the `auto_doc` tool with `dryRun: true` instead. ' +\n 'This tool is a read-only preview stub that returns `skipped` candidates without generating real docstrings.',\n usageHint:\n 'Deprecated: prefer `auto_doc` with `dryRun: true` for previewing, or `auto_doc` without dryRun for writing. ' +\n 'This tool only lists undocumented symbols with placeholder comments \u2014 it does not generate real JSDoc/TSDoc.',\n permission: 'auto',\n mutating: false,\n timeoutMs: 30_000,\n capabilities: ['fs.read'],\n icon: 'document',\n inputSchema: {\n type: 'object',\n properties: {\n target: {\n type: 'string',\n enum: ['file', 'function', 'class', 'type', 'all'],\n description: 'What to document',\n },\n path: {\n type: 'string',\n description: 'Specific file path to document',\n },\n files: {\n type: 'string',\n description: 'File(s) to process: single path, comma-separated list, or glob',\n },\n style: {\n type: 'string',\n enum: ['jsdoc', 'tsdoc', 'block'],\n description: 'Documentation style (default: jsdoc)',\n },\n\n cwd: { type: 'string', description: 'Working directory (default: cwd)' },\n },\n },\n async execute(input, ctx) {\n const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;\n const style = input.style ?? 'jsdoc';\n const results: DocumentedItem[] = [];\n let filesProcessed = 0;\n let itemsDocumented = 0;\n\n const fileList = input.files\n ? await resolveFiles(Array.isArray(input.files) ? input.files.join(',') : input.files, cwd)\n : input.path\n ? [safeResolve(input.path, ctx)]\n : [];\n\n for (const absPath of fileList) {\n try {\n const content = await fs.readFile(absPath, 'utf8');\n filesProcessed++;\n const processed = processFile(\n content,\n absPath,\n style,\n input.overwrite ?? false,\n input.target ?? 'all',\n );\n results.push(...processed);\n itemsDocumented += processed.filter((r) => r.status === 'documented').length;\n } catch (e) {\n results.push({\n path: absPath,\n name: absPath.split('/').pop() ?? absPath,\n signature: '',\n docstring: '',\n status: 'error',\n error: e instanceof Error ? e.message : String(e),\n });\n }\n }\n\n return {\n files_processed: filesProcessed,\n items_documented: itemsDocumented,\n results,\n style,\n };\n },\n};\n\nasync function resolveFiles(filesInput: string, cwd: string): Promise<string[]> {\n const files = Array.isArray(filesInput) ? filesInput : filesInput.split(',');\n const resolved: string[] = [];\n\n for (const f of files) {\n const absPath = f.trim().startsWith('/') ? f.trim() : `${cwd}/${f.trim()}`;\n try {\n const stat = await fs.stat(absPath);\n if (stat.isFile()) resolved.push(absPath);\n } catch {\n // skip\n }\n }\n\n return resolved;\n}\n\nfunction processFile(\n content: string,\n absPath: string,\n _style: string,\n _overwrite: boolean,\n target: string,\n): DocumentedItem[] {\n const results: DocumentedItem[] = [];\n // These must be global \u2014 `String.prototype.matchAll` throws on a non-global\n // RegExp, which previously made processFile throw and report every file as an\n // error instead of returning documentation candidates.\n const functionRegex = /(?:async\\s+)?function\\s+(\\w+)\\s*\\(([^)]*)\\)/g;\n const arrowRegex = /(?:const|let|var)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?\\(([^)]*)\\)\\s*=>/g;\n const classRegex = /class\\s+(\\w+)/g;\n const typeRegex = /(?:type|interface)\\s+(\\w+)\\s*[=<]/g;\n\n const allMatches: { name: string; sig: string; type: string; line: number }[] = [];\n\n if (target === 'all' || target === 'function') {\n for (const m of content.matchAll(functionRegex)) {\n /* v8 ignore next -- capture group (\\w+) is mandatory, so m[1] is always defined; defensive. */\n if (!m[1]) continue;\n allMatches.push({\n name: m[1],\n sig: m[2] ?? '',\n type: 'function',\n line: content.slice(0, m.index).split('\\n').length,\n });\n }\n for (const m of content.matchAll(arrowRegex)) {\n /* v8 ignore next -- capture group (\\w+) is mandatory, so m[1] is always defined; defensive. */\n if (!m[1]) continue;\n allMatches.push({\n name: m[1],\n sig: m[2] ?? '',\n type: 'arrow',\n line: content.slice(0, m.index).split('\\n').length,\n });\n }\n }\n\n if (target === 'all' || target === 'class') {\n for (const m of content.matchAll(classRegex)) {\n /* v8 ignore next -- capture group (\\w+) is mandatory, so m[1] is always defined; defensive. */\n if (!m[1]) continue;\n allMatches.push({\n name: m[1],\n sig: '',\n type: 'class',\n line: content.slice(0, m.index).split('\\n').length,\n });\n }\n }\n\n if (target === 'all' || target === 'type') {\n for (const m of content.matchAll(typeRegex)) {\n /* v8 ignore next -- capture group (\\w+) is mandatory, so m[1] is always defined; defensive. */\n if (!m[1]) continue;\n allMatches.push({\n name: m[1],\n sig: m[0] ?? '',\n type: 'type',\n line: content.slice(0, m.index).split('\\n').length,\n });\n }\n }\n\n for (const m of allMatches) {\n results.push({\n path: absPath,\n name: m.name,\n signature: m.sig,\n docstring: `/** ${m.name} - documented at line ${m.line} */`,\n status: 'skipped',\n });\n }\n\n return results;\n}\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core/utils';\nimport type { Context } from '@wrongstack/core/agent';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` \u2014 the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm \u2192 yarn \u2192 npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root\u2192out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink \u2014 macOS `/var`\u2192`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n\u2026[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]\u2026\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// \u2500\u2500\u2500 Command-output normalization (token-saving) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) \u2014 never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only \u2014 it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `\u2026 \u27E8repeated ${run}\u00D7\u27E9`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends \u2014 the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n\u2026[truncated ${total - kept} bytes]\u2026\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI \u2192 collapse\n * carriage-return progress \u2192 trim trailing whitespace \u2192 collapse identical\n * consecutive lines \u2192 squeeze blank-line runs \u2192 head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines \u2192 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["stat"]
|
|
7
|
-
}
|
package/dist/e2e.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"e2e.d.ts","sourceRoot":"","sources":["../src/e2e.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAGnD,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;AACpD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;AAEhE,UAAU,YAAY;IACpB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,CAAC;IAC/C,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,YAAY,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,EAAE,iBAAiB,CAAC;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA0XD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACP,SAAS,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,MAAM,EAAE,WAAW,CAAC;CACrB,GACA,OAAO,CAAC,aAAa,CAAC,CAuHxB;AAED,eAAO,MAAM,WAAW,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,CA4CzD,CAAC"}
|
package/dist/e2e.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/e2e.ts", "../src/_util.ts"],
|
|
4
|
-
"sourcesContent": ["import { open, readdir } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { Tool } from '@wrongstack/core/types';\nimport { safeResolveReal } from './_util.js';\n\nexport type E2EFramework = 'playwright' | 'cypress';\nexport type E2EPackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';\n\ninterface E2EPlanInput {\n cwd?: string | undefined;\n framework?: E2EFramework | 'all' | undefined;\n maxDepth?: number | undefined;\n includeSpecs?: boolean | undefined;\n}\n\nexport interface E2EServerHint {\n kind: 'managed' | 'attach';\n command?: string | undefined;\n url?: string | undefined;\n port?: number | undefined;\n source: 'static-config';\n incomplete: boolean;\n}\n\nexport interface E2EExecutionPlan {\n command: string;\n args: string[];\n cwd: string;\n source: 'package-script' | 'framework-default';\n script?: string | undefined;\n}\n\nexport interface E2EProjectPlan {\n framework: E2EFramework;\n root: string;\n configPath?: string | undefined;\n packagePath?: string | undefined;\n packageManager: E2EPackageManager;\n evidence: string[];\n scripts: string[];\n serverHints: E2EServerHint[];\n specPattern: string;\n specCount: number;\n specSamples: string[];\n specsTruncated: boolean;\n execution: E2EExecutionPlan;\n warnings: string[];\n}\n\nexport interface E2EPlanOutput {\n root: string;\n projects: E2EProjectPlan[];\n scannedDirectories: number;\n scanTruncated: boolean;\n warnings: string[];\n}\n\ninterface ScanResult {\n configs: Array<{ framework: E2EFramework; absolutePath: string }>;\n packageFiles: string[];\n scannedDirectories: number;\n truncated: boolean;\n}\n\ninterface PackageInfo {\n path: string;\n scripts: Record<string, string>;\n dependencies: Set<string>;\n packageManager?: E2EPackageManager | undefined;\n}\n\nasync function readBoundedText(filePath: string, maxBytes: number): Promise<string | undefined> {\n let handle;\n try {\n handle = await open(filePath, 'r');\n const info = await handle.stat();\n if (!info.isFile() || info.size > maxBytes) return undefined;\n const buffer = Buffer.alloc(info.size);\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);\n return buffer.subarray(0, bytesRead).toString('utf8');\n } catch {\n return undefined;\n } finally {\n await handle?.close().catch(() => undefined);\n }\n}\n\nconst CONFIG_NAMES = new Map<string, E2EFramework>([\n ['playwright.config.ts', 'playwright'],\n ['playwright.config.js', 'playwright'],\n ['playwright.config.mts', 'playwright'],\n ['playwright.config.mjs', 'playwright'],\n ['playwright.config.cts', 'playwright'],\n ['playwright.config.cjs', 'playwright'],\n ['cypress.config.ts', 'cypress'],\n ['cypress.config.js', 'cypress'],\n ['cypress.config.mts', 'cypress'],\n ['cypress.config.mjs', 'cypress'],\n ['cypress.config.cts', 'cypress'],\n ['cypress.config.cjs', 'cypress'],\n ['cypress.json', 'cypress'],\n]);\n\nconst SKIP_DIRECTORIES = new Set([\n '.git',\n '.hg',\n '.svn',\n '.wrongstack',\n 'node_modules',\n 'dist',\n 'build',\n 'coverage',\n 'coverage-html',\n 'playwright-report',\n 'test-results',\n '.next',\n]);\n\nconst MAX_SCAN_DIRECTORIES = 4_000;\nconst MAX_PACKAGE_BYTES = 512 * 1024;\nconst MAX_CONFIG_BYTES = 512 * 1024;\nconst MAX_SPEC_SAMPLES = 20;\n\nfunction relativePath(root: string, target: string): string {\n const value = path.relative(root, target) || '.';\n return value.split(path.sep).join('/');\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction staticString(source: string, property: string): string | undefined {\n const key = escapeRegExp(property);\n const direct = source.match(\n new RegExp(`(?:['\"]?${key}['\"]?)\\\\s*:\\\\s*(['\"\\\\x60])([^'\"\\\\x60\\\\r\\\\n]*)\\\\1`),\n );\n if (direct?.[2] && !direct[2].includes('${')) return direct[2];\n\n const sameLineFallback = source.match(\n new RegExp(`(?:['\"]?${key}['\"]?)\\\\s*:[^\\\\r\\\\n]{0,300}?(['\"\\\\x60])([^'\"\\\\x60\\\\r\\\\n]*)\\\\1`),\n );\n return sameLineFallback?.[2] && !sameLineFallback[2].includes('${')\n ? sameLineFallback[2]\n : undefined;\n}\n\nfunction staticNumber(source: string, property: string): number | undefined {\n const key = escapeRegExp(property);\n const match = source.match(new RegExp(`(?:['\"]?${key}['\"]?)\\\\s*:\\\\s*(\\\\d{2,5})\\\\b`));\n return match?.[1] ? Number.parseInt(match[1], 10) : undefined;\n}\n\nfunction serverHints(framework: E2EFramework, source: string): E2EServerHint[] {\n if (framework === 'playwright' && /\\bwebServer\\s*:/.test(source)) {\n const command = staticString(source, 'command');\n const url = staticString(source, 'url');\n const port = staticNumber(source, 'port');\n return [\n {\n kind: 'managed',\n ...(command ? { command } : {}),\n ...(url ? { url } : {}),\n ...(port ? { port } : {}),\n source: 'static-config',\n incomplete: !command || (!url && !port),\n },\n ];\n }\n\n const baseUrl = staticString(source, 'baseURL') ?? staticString(source, 'baseUrl');\n return baseUrl\n ? [{ kind: 'attach', url: baseUrl, source: 'static-config', incomplete: false }]\n : [];\n}\n\nfunction configuredSpecPattern(framework: E2EFramework, source: string): string {\n if (framework === 'playwright') {\n const testDir = staticString(source, 'testDir') ?? './tests';\n return `${testDir.replace(/\\/$/, '')}/**/*.{spec,test}.{js,jsx,ts,tsx,mjs,cjs}`;\n }\n return (\n staticString(source, 'specPattern') ??\n 'cypress/{e2e,integration}/**/*.cy.{js,jsx,ts,tsx,mjs,cjs}'\n );\n}\n\nasync function scanWorkspace(\n root: string,\n maxDepth: number,\n signal: AbortSignal,\n): Promise<ScanResult> {\n const result: ScanResult = {\n configs: [],\n packageFiles: [],\n scannedDirectories: 0,\n truncated: false,\n };\n const queue: Array<{ directory: string; depth: number }> = [{ directory: root, depth: 0 }];\n\n while (queue.length > 0) {\n signal.throwIfAborted();\n const current = queue.shift();\n if (!current) break;\n result.scannedDirectories += 1;\n if (result.scannedDirectories > MAX_SCAN_DIRECTORIES) {\n result.truncated = true;\n break;\n }\n\n let entries;\n try {\n entries = await readdir(current.directory, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n signal.throwIfAborted();\n const absolutePath = path.join(current.directory, entry.name);\n if (entry.isFile()) {\n if (entry.name === 'package.json') result.packageFiles.push(absolutePath);\n const framework = CONFIG_NAMES.get(entry.name);\n if (framework) result.configs.push({ framework, absolutePath });\n } else if (\n entry.isDirectory() &&\n current.depth < maxDepth &&\n !SKIP_DIRECTORIES.has(entry.name) &&\n !entry.name.startsWith('coverage-') &&\n !entry.name.startsWith('cov-')\n ) {\n queue.push({ directory: absolutePath, depth: current.depth + 1 });\n }\n }\n }\n\n return result;\n}\n\nasync function readPackageInfo(packagePath: string): Promise<PackageInfo | undefined> {\n try {\n const raw = await readBoundedText(packagePath, MAX_PACKAGE_BYTES);\n if (raw === undefined) return undefined;\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const scripts =\n parsed.scripts && typeof parsed.scripts === 'object'\n ? Object.fromEntries(\n Object.entries(parsed.scripts as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n )\n : {};\n const dependencies = new Set<string>();\n for (const field of ['dependencies', 'devDependencies', 'optionalDependencies']) {\n const value = parsed[field];\n if (!value || typeof value !== 'object') continue;\n for (const name of Object.keys(value as Record<string, unknown>)) dependencies.add(name);\n }\n const declared =\n typeof parsed.packageManager === 'string' ? (parsed.packageManager.split('@')[0] ?? '') : '';\n const packageManager = ['pnpm', 'npm', 'yarn', 'bun'].includes(declared)\n ? (declared as E2EPackageManager)\n : undefined;\n return { path: packagePath, scripts, dependencies, packageManager };\n } catch {\n return undefined;\n }\n}\n\nfunction frameworkFromPackage(info: PackageInfo): E2EFramework[] {\n const found = new Set<E2EFramework>();\n if (info.dependencies.has('@playwright/test') || info.dependencies.has('playwright')) {\n found.add('playwright');\n }\n if (info.dependencies.has('cypress')) found.add('cypress');\n for (const command of Object.values(info.scripts)) {\n if (/\\bplaywright(?:\\.cmd)?\\b/i.test(command)) found.add('playwright');\n if (/\\bcypress(?:\\.cmd)?\\b/i.test(command)) found.add('cypress');\n }\n return [...found];\n}\n\nasync function detectPackageManager(\n projectRoot: string,\n scanRoot: string,\n declared?: E2EPackageManager,\n): Promise<E2EPackageManager> {\n if (declared) return declared;\n let directory = projectRoot;\n while (true) {\n const names = new Set<string>();\n try {\n for (const entry of await readdir(directory)) names.add(entry);\n } catch {\n // Continue toward the scan root.\n }\n if (names.has('pnpm-lock.yaml')) return 'pnpm';\n if (names.has('yarn.lock')) return 'yarn';\n if (names.has('bun.lock') || names.has('bun.lockb')) return 'bun';\n if (names.has('package-lock.json') || names.has('npm-shrinkwrap.json')) return 'npm';\n if (directory === scanRoot) break;\n const parent = path.dirname(directory);\n const relativeParent = path.relative(scanRoot, parent);\n if (\n parent === directory ||\n relativeParent.startsWith('..') ||\n path.isAbsolute(relativeParent)\n ) {\n break;\n }\n directory = parent;\n }\n return 'npm';\n}\n\nfunction matchingScripts(info: PackageInfo | undefined, framework: E2EFramework): string[] {\n if (!info) return [];\n const matcher =\n framework === 'playwright' ? /\\bplaywright(?:\\.cmd)?\\b/i : /\\bcypress(?:\\.cmd)?\\b/i;\n return Object.entries(info.scripts)\n .filter(([, command]) => matcher.test(command))\n .map(([name]) => name)\n .sort((a, b) => {\n const preferred = ['test:e2e', 'e2e', 'e2e:test'];\n const ai = preferred.indexOf(a);\n const bi = preferred.indexOf(b);\n if (ai !== -1 || bi !== -1) return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);\n return a.localeCompare(b);\n });\n}\n\nfunction executionPlan(\n framework: E2EFramework,\n manager: E2EPackageManager,\n root: string,\n configPath: string | undefined,\n scripts: string[],\n): E2EExecutionPlan {\n const script = scripts[0];\n if (script) {\n return {\n command: manager,\n args: ['run', script],\n cwd: root,\n source: 'package-script',\n script,\n };\n }\n\n const runnerArgs = framework === 'playwright' ? ['playwright', 'test'] : ['cypress', 'run'];\n if (configPath) {\n runnerArgs.push(framework === 'playwright' ? '--config' : '--config-file', configPath);\n }\n return {\n command: manager === 'bun' ? 'bunx' : manager,\n args:\n manager === 'bun'\n ? runnerArgs\n : manager === 'npm'\n ? ['exec', '--', ...runnerArgs]\n : ['exec', ...runnerArgs],\n cwd: root,\n source: 'framework-default',\n };\n}\n\nfunction isSpec(framework: E2EFramework, filename: string): boolean {\n if (framework === 'cypress') return /\\.cy\\.(?:[cm]?[jt]sx?)$/i.test(filename);\n return /\\.(?:spec|test)\\.(?:[cm]?[jt]sx?)$/i.test(filename);\n}\n\nasync function collectSpecs(\n root: string,\n framework: E2EFramework,\n testDirectory: string | undefined,\n signal: AbortSignal,\n): Promise<{ count: number; samples: string[]; truncated: boolean }> {\n const roots = testDirectory\n ? [path.resolve(root, testDirectory)]\n : framework === 'cypress'\n ? [path.join(root, 'cypress', 'e2e'), path.join(root, 'cypress', 'integration')]\n : [path.join(root, 'tests')];\n const samples: string[] = [];\n let count = 0;\n let scanned = 0;\n const queue = [...roots];\n\n while (queue.length > 0) {\n signal.throwIfAborted();\n const directory = queue.shift();\n if (!directory) break;\n scanned += 1;\n if (scanned > MAX_SCAN_DIRECTORIES) return { count, samples, truncated: true };\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n signal.throwIfAborted();\n const target = path.join(directory, entry.name);\n if (entry.isDirectory() && !SKIP_DIRECTORIES.has(entry.name)) queue.push(target);\n else if (entry.isFile() && isSpec(framework, entry.name)) {\n count += 1;\n if (samples.length < MAX_SPEC_SAMPLES) samples.push(relativePath(root, target));\n }\n }\n }\n return { count, samples, truncated: count > samples.length };\n}\n\nasync function readConfig(configPath: string | undefined): Promise<string> {\n if (!configPath) return '';\n return (await readBoundedText(configPath, MAX_CONFIG_BYTES)) ?? '';\n}\n\nfunction nearestPackage(\n projectRoot: string,\n packagesByDirectory: Map<string, PackageInfo>,\n scanRoot: string,\n): PackageInfo | undefined {\n let directory = projectRoot;\n while (true) {\n const found = packagesByDirectory.get(directory);\n if (found) return found;\n if (directory === scanRoot) return undefined;\n const parent = path.dirname(directory);\n if (parent === directory || relativePath(scanRoot, parent).startsWith('..')) return undefined;\n directory = parent;\n }\n}\n\nexport async function discoverE2EProjects(\n root: string,\n options: {\n framework?: E2EFramework | 'all' | undefined;\n maxDepth?: number | undefined;\n includeSpecs?: boolean | undefined;\n signal: AbortSignal;\n },\n): Promise<E2EPlanOutput> {\n const maxDepth = Math.max(0, Math.min(options.maxDepth ?? 5, 8));\n const scan = await scanWorkspace(root, maxDepth, options.signal);\n const packages = (await Promise.all(scan.packageFiles.map(readPackageInfo))).filter(\n (info): info is PackageInfo => Boolean(info),\n );\n const packagesByDirectory = new Map(packages.map((info) => [path.dirname(info.path), info]));\n const candidates = new Map<\n string,\n { framework: E2EFramework; root: string; configPath?: string }\n >();\n\n for (const config of scan.configs) {\n if (\n options.framework &&\n options.framework !== 'all' &&\n config.framework !== options.framework\n ) {\n continue;\n }\n const projectRoot = path.dirname(config.absolutePath);\n candidates.set(`${config.framework}:${projectRoot}`, {\n framework: config.framework,\n root: projectRoot,\n configPath: config.absolutePath,\n });\n }\n\n for (const info of packages) {\n const projectRoot = path.dirname(info.path);\n for (const framework of frameworkFromPackage(info)) {\n if (options.framework && options.framework !== 'all' && framework !== options.framework)\n continue;\n const key = `${framework}:${projectRoot}`;\n if (!candidates.has(key)) candidates.set(key, { framework, root: projectRoot });\n }\n }\n\n const projects: E2EProjectPlan[] = [];\n for (const candidate of candidates.values()) {\n options.signal.throwIfAborted();\n const info = nearestPackage(candidate.root, packagesByDirectory, root);\n const source = await readConfig(candidate.configPath);\n const scripts = matchingScripts(info, candidate.framework);\n const manager = await detectPackageManager(candidate.root, root, info?.packageManager);\n const testDirectory =\n candidate.framework === 'playwright' ? staticString(source, 'testDir') : undefined;\n const resolvedTestDirectory = testDirectory\n ? path.resolve(candidate.root, testDirectory)\n : undefined;\n const relativeTestDirectory = resolvedTestDirectory\n ? path.relative(root, resolvedTestDirectory)\n : undefined;\n const unsafeTestDirectory = Boolean(\n relativeTestDirectory &&\n (relativeTestDirectory.startsWith('..') || path.isAbsolute(relativeTestDirectory)),\n );\n const specs =\n options.includeSpecs === false || unsafeTestDirectory\n ? { count: 0, samples: [], truncated: false }\n : await collectSpecs(candidate.root, candidate.framework, testDirectory, options.signal);\n const warnings: string[] = [];\n if (!candidate.configPath)\n warnings.push('Framework dependency or script found without a config file.');\n if (candidate.configPath && !source) {\n warnings.push('Config could not be read or exceeded the static inspection limit.');\n }\n if (unsafeTestDirectory) {\n warnings.push(\n 'Configured testDir resolves outside the requested discovery root and was not scanned.',\n );\n }\n const hints = serverHints(candidate.framework, source);\n if (hints.some((hint) => hint.incomplete)) {\n warnings.push('Configured server contains dynamic values; only static hints are shown.');\n }\n\n projects.push({\n framework: candidate.framework,\n root: relativePath(root, candidate.root),\n ...(candidate.configPath ? { configPath: relativePath(root, candidate.configPath) } : {}),\n ...(info ? { packagePath: relativePath(root, info.path) } : {}),\n packageManager: manager,\n evidence: [\n ...(candidate.configPath ? [`config:${relativePath(root, candidate.configPath)}`] : []),\n ...(info?.dependencies.has(\n candidate.framework === 'playwright' ? '@playwright/test' : 'cypress',\n )\n ? [`dependency:${candidate.framework === 'playwright' ? '@playwright/test' : 'cypress'}`]\n : []),\n ...scripts.map((script) => `script:${script}`),\n ],\n scripts,\n serverHints: hints,\n specPattern: configuredSpecPattern(candidate.framework, source),\n specCount: specs.count,\n specSamples: specs.samples,\n specsTruncated: specs.truncated,\n execution: executionPlan(\n candidate.framework,\n manager,\n relativePath(root, candidate.root),\n candidate.configPath ? relativePath(candidate.root, candidate.configPath) : undefined,\n scripts,\n ),\n warnings,\n });\n }\n\n projects.sort((a, b) => a.root.localeCompare(b.root) || a.framework.localeCompare(b.framework));\n return {\n root,\n projects,\n scannedDirectories: scan.scannedDirectories,\n scanTruncated: scan.truncated,\n warnings: scan.truncated\n ? [`Discovery stopped after ${MAX_SCAN_DIRECTORIES} directories; narrow cwd or maxDepth.`]\n : [],\n };\n}\n\nexport const e2ePlanTool: Tool<E2EPlanInput, E2EPlanOutput> = {\n name: 'e2e_plan',\n category: 'Code Quality',\n description:\n 'Discover Playwright and Cypress projects and preview bounded E2E execution plans without loading configs or starting processes.',\n usageHint:\n 'Use before running browser E2E tests. The result identifies authoritative configs, package scripts, ' +\n 'static server hints, specs, and exact argv. This tool never imports project config or executes commands.',\n permission: 'auto',\n mutating: false,\n riskTier: 'safe',\n capabilities: ['fs.read'],\n icon: 'test',\n timeoutMs: 30_000,\n inputSchema: {\n type: 'object',\n properties: {\n cwd: { type: 'string', description: 'Directory inside the project to inspect.' },\n framework: {\n type: 'string',\n enum: ['all', 'playwright', 'cypress'],\n description: 'Optional framework filter; defaults to all.',\n },\n maxDepth: {\n type: 'integer',\n minimum: 0,\n maximum: 8,\n description: 'Maximum workspace discovery depth; defaults to 5.',\n },\n includeSpecs: {\n type: 'boolean',\n description: 'Count and sample specs; defaults to true.',\n },\n },\n },\n async execute(input, ctx, opts) {\n const root = await safeResolveReal(input.cwd ?? '.', ctx);\n return discoverE2EProjects(root, {\n framework: input.framework,\n maxDepth: input.maxDepth,\n includeSpecs: input.includeSpecs,\n signal: opts.signal,\n });\n },\n};\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core/utils';\nimport type { Context } from '@wrongstack/core/agent';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` \u2014 the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm \u2192 yarn \u2192 npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root\u2192out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink \u2014 macOS `/var`\u2192`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n\u2026[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]\u2026\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// \u2500\u2500\u2500 Command-output normalization (token-saving) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) \u2014 never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only \u2014 it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `\u2026 \u27E8repeated ${run}\u00D7\u27E9`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends \u2014 the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n\u2026[truncated ${total - kept} bytes]\u2026\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI \u2192 collapse\n * carriage-return progress \u2192 trim trailing whitespace \u2192 collapse identical\n * consecutive lines \u2192 squeeze blank-line runs \u2192 head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines \u2192 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["path"]
|
|
7
|
-
}
|
package/dist/edit.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AASnD,OAAO,EAIL,KAAK,SAAS,EAIf,MAAM,kBAAkB,CAAC;AAI1B,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACnC;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,eAAO,MAAM,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CAsRhD,CAAC"}
|
package/dist/edit.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/edit.ts", "../src/_edit-match.ts", "../src/_syntax-check.ts", "../src/_util.ts"],
|
|
4
|
-
"sourcesContent": ["import * as fs from 'node:fs/promises';\nimport type { Tool } from '@wrongstack/core/types';\nimport {\n atomicWrite,\n detectNewlineStyle,\n normalizeToLf,\n toStyle,\n unifiedDiff,\n} from '@wrongstack/core/utils';\nimport { ToolValidationError } from '@wrongstack/core/types';\nimport {\n adjustIndent,\n findLadderMatches,\n firstLineIndent,\n type MatchTier,\n nearestMatchHint,\n TIER_CONFIDENCE,\n TIER_LABEL,\n} from './_edit-match.js';\nimport { checkSyntax } from './_syntax-check.js';\nimport { safeResolveReal, sha256hex } from './_util.js';\n\ninterface EditInput {\n path: string;\n old_string: string;\n new_string: string;\n /**\n * When true, replaces all occurrences of `old_string`.\n * When false (default), replaces only the first occurrence and errors\n * if more than one match exists \u2014 use this to ensure you target the\n * right location.\n */\n replace_all?: boolean | undefined;\n}\n\ninterface EditOutput {\n path: string;\n replacements: number;\n diff: string;\n /**\n * How `old_string` was located. Anything other than 'exact' means a\n * whitespace/fuzzy fallback fired \u2014 the note carries the confidence level\n * and the diff shows exactly what was replaced.\n */\n matched_by: MatchTier;\n /**\n * Parse errors found in the file AFTER the edit was applied (TS/JS/JSON\n * only). The edit is still on disk \u2014 fix these in a follow-up edit now,\n * before doing anything else.\n */\n syntax_errors?: string[] | undefined;\n note?: string | undefined;\n}\n\nexport const editTool: Tool<EditInput, EditOutput> = {\n name: 'edit',\n category: 'Filesystem',\n description:\n 'Perform a precise, surgical text replacement in a file. This is the preferred tool for modifying existing code. ' +\n 'It works best after a prior `read`, but can auto-read the current file when the replacement is still unambiguous. ' +\n 'Fails safely if the `old_string` appears more than once unless `replace_all` is set.',\n usageHint:\n 'RECOMMENDED WORKFLOW:\\n' +\n '1. Prefer calling `read` on the target file first when planning an edit.\\n' +\n '2. Use a sufficiently unique `old_string` (include surrounding lines/context if needed).\\n' +\n '3. If the string appears multiple times and you want to change all of them, set `replace_all: true`.\\n' +\n '4. `new_string` must be the exact replacement text.\\n\\n' +\n 'If no prior read is recorded, the tool auto-reads the current file and only applies the edit after the same ambiguity checks pass.\\n' +\n 'If `old_string` differs from the file only in whitespace (trailing spaces, indentation), a lower-confidence fallback match is applied and reported in `matched_by`/`note` \u2014 always verify the diff when this happens.\\n' +\n 'After the edit, TS/JS/JSON files are syntax-checked; if `syntax_errors` is present in the output, fix those errors immediately with a follow-up edit.',\n selection: {\n doNotUseWhen: 'creating a new file, replacing the whole file, or applying an existing unified diff.',\n useInstead: ['write', 'patch'],\n },\n permission: 'confirm',\n mutating: true,\n capabilities: ['fs.write'],\n icon: 'edit',\n timeoutMs: 5_000,\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string' },\n old_string: { type: 'string' },\n new_string: { type: 'string' },\n replace_all: { type: 'boolean' },\n },\n required: ['path', 'old_string', 'new_string'],\n },\n async execute(input, ctx, opts) {\n if (!input?.path) {\n throw new ToolValidationError({ message: 'edit: path is required', field: 'path' });\n }\n if (input.old_string === undefined) {\n throw new ToolValidationError({\n message: 'edit: old_string is required',\n field: 'old_string',\n });\n }\n if (input.new_string === undefined) {\n throw new ToolValidationError({\n message: 'edit: new_string is required',\n field: 'new_string',\n });\n }\n if (input.old_string === '') {\n throw new ToolValidationError({\n message: 'edit: old_string cannot be empty',\n field: 'old_string',\n });\n }\n\n const absPath = await safeResolveReal(input.path, ctx);\n const stat = await fs.stat(absPath).catch((err) => {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new ToolValidationError({\n message: `edit: file \"${input.path}\" does not exist. Use \\`write\\` instead.`,\n field: 'path',\n context: { exists: false },\n });\n }\n throw err;\n });\n if (!stat.isFile()) {\n throw new ToolValidationError({\n message: `edit: \"${input.path}\" is not a regular file`,\n field: 'path',\n });\n }\n\n const autoRead = !ctx.hasRead(absPath);\n // Read BEFORE mtime check to eliminate TOCTOU window.\n // The sequence must be: read content \u2192 check mtime \u2192 apply edit.\n // If we check mtime first, a concurrent modification between the\n // stat call and the read gives us stale content to search/replace.\n const original = await fs.readFile(absPath, 'utf8');\n const updated = await fs.stat(absPath);\n const mtimeTolerance = process.platform === 'win32' ? 2000 : 1;\n const originalHash = sha256hex(original);\n // Optional call: embedders and older test fixtures may hand in a\n // duck-typed Context without hash tracking \u2014 they fall back to mtime.\n const lastReadHash = ctx.lastReadHash?.(absPath);\n if (lastReadHash !== undefined) {\n // Content hash is the authoritative arbiter when available: it closes\n // the mtime tolerance window (an external write within 2 s of the read\n // on Windows is invisible to mtime) and ignores content-preserving\n // mtime bumps (touch, checkout of identical content).\n if (lastReadHash !== originalHash) {\n throw new ToolValidationError({\n message: `edit: file \"${input.path}\" was modified externally. Re-read it first.`,\n field: 'path',\n context: { reason: 'external_modification' },\n });\n }\n } else {\n const lastReadMtime = ctx.lastReadMtime(absPath);\n if (lastReadMtime !== undefined && updated.mtimeMs > lastReadMtime + mtimeTolerance) {\n throw new ToolValidationError({\n message: `edit: file \"${input.path}\" was modified externally. Re-read it first.`,\n field: 'path',\n context: { reason: 'external_modification' },\n });\n }\n }\n if (autoRead && updated.mtimeMs > stat.mtimeMs + mtimeTolerance) {\n throw new ToolValidationError({\n message: `edit: file \"${input.path}\" changed while being auto-read. Retry the edit.`,\n field: 'path',\n context: { reason: 'auto_read_race' },\n });\n }\n const autoReadNote = autoRead\n ? `No prior read was recorded for \"${input.path}\"; edit auto-read the current file and applied the replacement only after the ambiguity checks passed.`\n : undefined;\n const style = detectNewlineStyle(original);\n const fileLf = normalizeToLf(original);\n const oldLf = normalizeToLf(input.old_string);\n const newLf = normalizeToLf(input.new_string);\n\n if (oldLf === newLf) {\n if (autoRead) ctx.recordRead(absPath, updated.mtimeMs, 'user', originalHash);\n return {\n path: absPath,\n replacements: 0,\n diff: '(no-op: old and new are identical)',\n matched_by: 'exact',\n note: autoReadNote,\n };\n }\n\n // Check abort signal before entering potentially slow matching logic\n opts?.signal?.throwIfAborted();\n const ladder = findLadderMatches(fileLf, oldLf);\n\n if (!ladder) {\n opts?.signal?.throwIfAborted();\n const hint = nearestMatchHint(fileLf, oldLf);\n throw new ToolValidationError({\n message: `edit: no match for old_string in \"${input.path}\".${\n hint\n ? ` Nearest match near line ${hint.line}:\\n${hint.snippet}\\n` +\n `Compare this against your old_string and retry with the file's actual text.`\n : ''\n }`,\n field: 'old_string',\n });\n }\n\n const { tier, matches } = ladder;\n const count = matches.length;\n\n if (ladder.ambiguous) {\n const lines = matches.map((m) => m.startLine);\n throw new ToolValidationError({\n message:\n `edit: old_string only matched fuzzily and ${count} candidate blocks scored too close ` +\n `to distinguish (lines: ${lines.join(', ')}) in \"${input.path}\". ` +\n `Re-read the file and use the exact text of the intended block.`,\n field: 'old_string',\n context: { occurrences: count, matchTier: tier },\n });\n }\n\n // Fuzzy/indent-insensitive matches are single-target operations: applying\n // them in bulk multiplies a low-confidence guess. replace_all therefore\n // requires at least a trailing-whitespace-level match.\n if (input.replace_all && tier !== 'exact' && tier !== 'trailing-whitespace') {\n throw new ToolValidationError({\n message:\n `edit: old_string only matched via ${TIER_LABEL[tier]} in \"${input.path}\", ` +\n `but replace_all requires an exact (or trailing-whitespace) match. ` +\n `Re-read the file and use its exact text.`,\n field: 'old_string',\n context: { matchTier: tier },\n });\n }\n\n if (count > 1 && !input.replace_all) {\n const lines = matches.map((m) => m.startLine);\n throw new ToolValidationError({\n message:\n `edit: old_string matched ${count} times in \"${input.path}\" (lines: ${lines.join(', ')})` +\n `${tier === 'exact' ? '' : ` via ${TIER_LABEL[tier]}`}. ` +\n `Add more context to make it unique, or set replace_all: true.`,\n field: 'old_string',\n context: { occurrences: count, matchTier: tier },\n });\n }\n\n let newFileLf: string;\n let tierNote: string | undefined;\n if (tier === 'exact') {\n newFileLf = input.replace_all\n ? fileLf.split(oldLf).join(newLf)\n : fileLf.replace(oldLf, newLf);\n } else {\n // Fallback tiers matched whole-line windows \u2014 splice by char offsets,\n // right to left so earlier offsets stay valid. For indent-insensitive\n // tiers, shift the replacement to the file's actual indentation.\n let replacement = newLf;\n let indentSuffix = '';\n if (tier === 'whitespace-normalized' || tier === 'fuzzy') {\n const first = matches[0] as (typeof matches)[number];\n const matchedText = fileLf.slice(first.start, first.end);\n const adjusted = adjustIndent(newLf, firstLineIndent(oldLf), firstLineIndent(matchedText));\n replacement = adjusted.text;\n if (adjusted.adjusted) indentSuffix = '; replacement re-indented to match the file';\n }\n newFileLf = fileLf;\n const applied = input.replace_all ? matches : matches.slice(0, 1);\n for (let i = applied.length - 1; i >= 0; i--) {\n const m = applied[i] as (typeof matches)[number];\n newFileLf = newFileLf.slice(0, m.start) + replacement + newFileLf.slice(m.end);\n }\n const scoreSuffix =\n ladder.score !== undefined ? `, similarity ${(ladder.score * 100).toFixed(1)}%` : '';\n tierNote =\n `old_string did not match exactly; applied via ${TIER_LABEL[tier]} at line ` +\n `${(matches[0] as (typeof matches)[number]).startLine} ` +\n `(confidence: ${TIER_CONFIDENCE[tier]}${scoreSuffix}${indentSuffix}). ` +\n `Review the diff to confirm the intended target was edited.`;\n }\n const newFile = toStyle(newFileLf, style);\n\n // Last exit before mutating the filesystem: a run aborted during the\n // read/match phase must not leave the edit behind. (atomicWrite itself\n // is all-or-nothing \u2014 the file is old or new, never partial.)\n opts?.signal?.throwIfAborted();\n await atomicWrite(absPath, newFile, { mode: updated.mode & 0o777 });\n const written = await fs.stat(absPath);\n // Record mtime + content hash so a later edit detects external\n // modification, but tag as 'write' so the permission policy's\n // write-smart-bypass does NOT treat this as \"user already saw the\n // content\" (P1 #1).\n ctx.recordRead(absPath, written.mtimeMs, 'write', sha256hex(newFile));\n\n // Record for session rewind\n ctx.session.recordFileChange({\n path: absPath,\n action: 'modified',\n before: original,\n after: newFile,\n });\n\n // Check abort before diff generation (can be slow on large files)\n opts?.signal?.throwIfAborted();\n const diff = unifiedDiff(original, newFile, {\n fromFile: input.path,\n toFile: input.path,\n });\n\n // Post-edit syntax validation (TS/JS/JSON). The edit stays on disk \u2014\n // errors come back in the same turn so the model fixes them immediately.\n const syntax = await checkSyntax(absPath, newFile, original).catch(() => undefined);\n let syntaxNote: string | undefined;\n if (syntax && syntax.errors.length > 0) {\n syntaxNote = syntax.preExisting\n ? `Syntax check: the file still has parse errors (they pre-date this edit) \u2014 see syntax_errors.`\n : `Syntax check: this edit introduced ${syntax.errors.length} parse error(s) \u2014 fix them now, see syntax_errors.`;\n }\n\n const notes = [autoReadNote, tierNote, syntaxNote].filter(Boolean);\n\n return {\n path: absPath,\n replacements: input.replace_all ? count : 1,\n diff,\n matched_by: tier,\n syntax_errors: syntax && syntax.errors.length > 0 ? syntax.errors : undefined,\n note: notes.length > 0 ? notes.join('\\n') : undefined,\n };\n },\n};\n", "/**\n * Matching ladder for the `edit` tool.\n *\n * Models routinely hallucinate whitespace when reproducing code they read\n * many turns ago: trailing spaces appear, indentation shifts by a level,\n * tabs become spaces. A strict exact-match `old_string` fails on all of\n * these even though the intended target is unambiguous. This module\n * implements a graded fallback chain \u2014 each tier is strictly more permissive\n * and strictly lower-confidence than the previous one:\n *\n * 1. `exact` \u2014 substring match (the classic behavior).\n * 2. `trailing-whitespace` \u2014 whole-line window match ignoring trailing\n * whitespace per line. Zero ambiguity risk\n * beyond exact (trailing blanks never carry\n * meaning in the languages we edit).\n * 3. `whitespace-normalized` \u2014 whole-line window match ignoring leading\n * AND trailing whitespace per line. The\n * replacement is re-indented to the file's\n * actual indentation.\n * 4. `fuzzy` \u2014 block-anchor match: first and last lines\n * must match (trimmed), interior lines are\n * compared by Levenshtein similarity. Only a\n * single candidate with a clear margin over\n * the runner-up is accepted.\n *\n * The caller (edit.ts) enforces per-tier uniqueness, restricts `replace_all`\n * to tiers 1\u20132, and surfaces the tier + confidence in the tool output so the\n * model and the session log both see when a fallback fired.\n */\n\nexport type MatchTier = 'exact' | 'trailing-whitespace' | 'whitespace-normalized' | 'fuzzy';\n\nexport interface LadderMatch {\n /** Char offset (inclusive) of the match start in the LF-normalized file. */\n start: number;\n /** Char offset (exclusive) of the match end. */\n end: number;\n /** 1-based line number of the first matched line (for error messages). */\n startLine: number;\n}\n\nexport interface LadderResult {\n tier: MatchTier;\n matches: LadderMatch[];\n /** Similarity score of the best candidate \u2014 only set for the fuzzy tier. */\n score?: number | undefined;\n /**\n * Fuzzy tier only: true when two candidates scored within the ambiguity\n * margin of each other, so no single target can be trusted. `matches`\n * then contains the rival candidates for the error message.\n */\n ambiguous?: boolean | undefined;\n}\n\n/** Human-readable label per tier, used in notes and error messages. */\nexport const TIER_LABEL: Record<MatchTier, string> = {\n exact: 'exact match',\n 'trailing-whitespace': 'whitespace-normalized match (trailing whitespace ignored)',\n 'whitespace-normalized':\n 'whitespace-normalized match (indentation ignored, replacement re-indented)',\n fuzzy: 'fuzzy match (block anchors exact, interior \u226590% similar)',\n};\n\n/** Confidence per tier, recorded in the tool output note. */\nexport const TIER_CONFIDENCE: Record<MatchTier, 'exact' | 'high' | 'medium' | 'low'> = {\n exact: 'exact',\n 'trailing-whitespace': 'high',\n 'whitespace-normalized': 'medium',\n fuzzy: 'low',\n};\n\n/** Below this many non-whitespace chars, indent-insensitive tiers are too\n * ambiguous to trust (`}` would match everywhere). */\nconst MIN_NORMALIZED_NEEDLE_CHARS = 16;\n/** Fuzzy interior similarity threshold. */\nconst FUZZY_MIN_SIMILARITY = 0.9;\n/** A fuzzy best-candidate must beat the runner-up by at least this margin. */\nconst FUZZY_AMBIGUITY_MARGIN = 0.05;\n/** Cost guard: skip fuzzy comparison on interiors larger than this. */\nconst FUZZY_MAX_INTERIOR_CHARS = 2000;\n\n/**\n * Walk the ladder and return the first tier that produces at least one\n * match. Returns `undefined` when no tier matches.\n */\nexport function findLadderMatches(fileLf: string, oldLf: string): LadderResult | undefined {\n // Tier 1 \u2014 exact substring scan (supports sub-line needles).\n const exact: LadderMatch[] = [];\n let idx = fileLf.indexOf(oldLf);\n while (idx !== -1) {\n exact.push({ start: idx, end: idx + oldLf.length, startLine: lineAt(fileLf, idx) });\n idx = fileLf.indexOf(oldLf, idx + 1);\n }\n if (exact.length > 0) return { tier: 'exact', matches: exact };\n\n // Line-based tiers operate on whole-line windows.\n const fileLines = fileLf.split('\\n');\n const needleLines = oldLf.split('\\n');\n if (needleLines.length > fileLines.length) return undefined;\n const offsets = lineOffsets(fileLines);\n\n // Pre-trim file lines ONCE so the sliding window doesn't call trimEnd()/\n // trim() on every (fileLine \u00D7 needleLine) pair \u2014 avoiding O(n\u00B7m) string\n // allocations for every tier. For a 10 000-line \u00D7 10-line needle that\n // saves ~200 000 trim() calls per tier.\n const fileTrimEnd = fileLines.map((l) => l.trimEnd());\n const needleTrimEnd = needleLines.map((l) => l.trimEnd());\n\n const trailing = windowScan(\n fileTrimEnd,\n needleTrimEnd,\n offsets,\n fileLines,\n (a, b) => a === b,\n );\n if (trailing.length > 0) return { tier: 'trailing-whitespace', matches: trailing };\n\n const normalizedLen = needleTrimEnd.reduce((n, l) => n + l.trimStart().length, 0);\n if (normalizedLen < MIN_NORMALIZED_NEEDLE_CHARS) return undefined;\n\n const fileTrimmed = fileTrimEnd.map((l) => l.trimStart());\n const needleTrimmed = needleTrimEnd.map((l) => l.trimStart());\n const normalized = windowScan(\n fileTrimmed,\n needleTrimmed,\n offsets,\n fileLines,\n (a, b) => a === b,\n );\n if (normalized.length > 0) return { tier: 'whitespace-normalized', matches: normalized };\n\n return fuzzyScan(fileLines, needleLines, offsets);\n}\n\n/** 1-based line number containing char offset `pos`.\n * Uses indexOf('\\n') for O(log n) instead of scanning char-by-char.\n * For small pos (< 512 chars) the simple loop is faster, so we keep that\n * fast path for the common case (single-line needles, small files). */\nfunction lineAt(text: string, pos: number): number {\n if (pos < 512) {\n let line = 1;\n for (let i = 0; i < pos; i++) {\n if (text.charCodeAt(i) === 0x0a) line++;\n }\n return line;\n }\n // For large offsets, binary-search through newline positions.\n let line = 1;\n let search = 0;\n while (true) {\n const idx = text.indexOf('\\n', search);\n if (idx === -1 || idx >= pos) return line;\n search = idx + 1;\n line++;\n }\n}\n\n/** Start char offset of each line in the LF-normalized text. */\nfunction lineOffsets(lines: string[]): number[] {\n const out = new Array<number>(lines.length);\n let pos = 0;\n for (let i = 0; i < lines.length; i++) {\n out[i] = pos;\n pos += (lines[i] as string).length + 1; // +1 for the '\\n'\n }\n return out;\n}\n\nfunction windowToMatch(\n fileLines: string[],\n offsets: number[],\n start: number,\n windowLen: number,\n): LadderMatch {\n const lastLine = start + windowLen - 1;\n return {\n start: offsets[start] as number,\n end: (offsets[lastLine] as number) + (fileLines[lastLine] as string).length,\n startLine: start + 1,\n };\n}\n\n/** Slide a needle-sized window over the file lines with a per-line comparator.\n * Matched windows never overlap (scan resumes after the window). */\nfunction windowScan(\n comparisonLines: string[],\n needleLines: string[],\n offsets: number[],\n originalLines: string[],\n eq: (fileLine: string, needleLine: string) => boolean,\n): LadderMatch[] {\n const n = needleLines.length;\n const out: LadderMatch[] = [];\n for (let i = 0; i + n <= comparisonLines.length; i++) {\n let all = true;\n for (let j = 0; j < n; j++) {\n if (!eq(comparisonLines[i + j] as string, needleLines[j] as string)) {\n all = false;\n break;\n }\n }\n if (all) {\n out.push(windowToMatch(originalLines, offsets, i, n));\n i += n - 1; // no overlapping windows\n }\n }\n return out;\n}\n\n/** Block-anchor fuzzy tier: first/last lines trim-exact, interior by\n * Levenshtein similarity. Requires \u22653 needle lines so anchors exist. */\nfunction fuzzyScan(\n fileLines: string[],\n needleLines: string[],\n offsets: number[],\n): LadderResult | undefined {\n const n = needleLines.length;\n if (n < 3) return undefined;\n const firstNeedle = (needleLines[0] as string).trim();\n const lastNeedle = (needleLines[n - 1] as string).trim();\n const needleInterior = needleLines\n .slice(1, -1)\n .map((l) => l.trim())\n .join('\\n');\n if (needleInterior.length > FUZZY_MAX_INTERIOR_CHARS) return undefined;\n\n const candidates: Array<{ match: LadderMatch; score: number }> = [];\n for (let i = 0; i + n <= fileLines.length; i++) {\n if ((fileLines[i] as string).trim() !== firstNeedle) continue;\n if ((fileLines[i + n - 1] as string).trim() !== lastNeedle) continue;\n const windowInterior = fileLines\n .slice(i + 1, i + n - 1)\n .map((l) => l.trim())\n .join('\\n');\n if (windowInterior.length > FUZZY_MAX_INTERIOR_CHARS) continue;\n const score = similarity(needleInterior, windowInterior);\n if (score >= FUZZY_MIN_SIMILARITY) {\n candidates.push({ match: windowToMatch(fileLines, offsets, i, n), score });\n }\n }\n\n if (candidates.length === 0) return undefined;\n candidates.sort((a, b) => b.score - a.score);\n const best = candidates[0] as { match: LadderMatch; score: number };\n const runnerUp = candidates[1];\n if (runnerUp && best.score - runnerUp.score < FUZZY_AMBIGUITY_MARGIN) {\n return {\n tier: 'fuzzy',\n matches: candidates.map((c) => c.match),\n score: best.score,\n ambiguous: true,\n };\n }\n return { tier: 'fuzzy', matches: [best.match], score: best.score };\n}\n\n/** Normalized similarity in [0, 1]: 1 \u2212 levenshtein / max-length. */\nexport function similarity(a: string, b: string): number {\n if (a === b) return 1;\n const max = Math.max(a.length, b.length);\n if (max === 0) return 1;\n // Quick reject: a length gap larger than the allowed edit budget can\n // never reach the threshold \u2014 skip the O(n\u00B7m) DP entirely.\n if (Math.abs(a.length - b.length) / max > 1 - FUZZY_MIN_SIMILARITY + 0.05) {\n return 1 - Math.abs(a.length - b.length) / max;\n }\n return 1 - levenshtein(a, b) / max;\n}\n\n/** Two-row Levenshtein distance. */\nfunction levenshtein(a: string, b: string): number {\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n let prev = new Array<number>(b.length + 1);\n let cur = new Array<number>(b.length + 1);\n for (let j = 0; j <= b.length; j++) prev[j] = j;\n for (let i = 1; i <= a.length; i++) {\n cur[0] = i;\n const ca = a.charCodeAt(i - 1);\n for (let j = 1; j <= b.length; j++) {\n const cost = ca === b.charCodeAt(j - 1) ? 0 : 1;\n cur[j] = Math.min(\n (prev[j] as number) + 1,\n (cur[j - 1] as number) + 1,\n (prev[j - 1] as number) + cost,\n );\n }\n [prev, cur] = [cur, prev];\n }\n return prev[b.length] as number;\n}\n\n/** Leading whitespace (spaces/tabs) of the first non-empty line, or ''. */\nexport function firstLineIndent(text: string): string {\n for (const line of text.split('\\n')) {\n if (line.trim() === '') continue;\n return (/^[ \\t]*/.exec(line) as RegExpExecArray)[0];\n }\n return '';\n}\n\n/**\n * Shift `newLf` from the needle's indentation to the matched block's actual\n * indentation. Only clean prefix relationships are adjusted (purely adding\n * or removing leading whitespace); a tabs-vs-spaces mismatch is left alone\n * rather than guessed at \u2014 the diff in the tool output makes the result\n * visible either way.\n */\nexport function adjustIndent(\n newLf: string,\n fromIndent: string,\n toIndent: string,\n): { text: string; adjusted: boolean } {\n if (fromIndent === toIndent) return { text: newLf, adjusted: false };\n const lines = newLf.split('\\n');\n if (toIndent.startsWith(fromIndent)) {\n const extra = toIndent.slice(fromIndent.length);\n return {\n text: lines.map((l) => (l.trim() === '' ? l : extra + l)).join('\\n'),\n adjusted: true,\n };\n }\n if (fromIndent.startsWith(toIndent)) {\n const remove = fromIndent.slice(toIndent.length);\n return {\n text: lines.map((l) => (l.startsWith(remove) ? l.slice(remove.length) : l)).join('\\n'),\n adjusted: true,\n };\n }\n return { text: newLf, adjusted: false };\n}\n\n/**\n * Best-effort locator for the no-match error: find the window whose trimmed\n * lines are most similar to the needle (cheap common-prefix scoring \u2014 this\n * runs on the failure path where the needle by definition doesn't match, so\n * precision matters less than never being slow). Returns the 1-based line\n * and a short snippet the model can use to correct itself without re-reading.\n */\nexport function nearestMatchHint(\n fileLf: string,\n oldLf: string,\n): { line: number; snippet: string } | undefined {\n const fileLines = fileLf.split('\\n');\n const needleLines = oldLf.split('\\n');\n if (needleLines.length > fileLines.length) return undefined;\n\n const needleTrimmed = needleLines.map((l) => l.trim());\n let bestScore = 0;\n let bestStart = -1;\n for (let i = 0; i + needleLines.length <= fileLines.length; i++) {\n let sum = 0;\n for (let j = 0; j < needleLines.length; j++) {\n sum += prefixSimilarity(needleTrimmed[j] as string, (fileLines[i + j] as string).trim());\n }\n const score = sum / needleLines.length;\n if (score > bestScore) {\n bestScore = score;\n bestStart = i;\n }\n }\n if (bestStart === -1 || bestScore < 0.5) return undefined;\n\n const snippet = fileLines\n .slice(bestStart, bestStart + Math.min(3, needleLines.length))\n .map((l) => (l.length > 120 ? `${l.slice(0, 120)}\u2026` : l))\n .join('\\n');\n return { line: bestStart + 1, snippet };\n}\n\n/** Cheap similarity proxy: shared prefix length over max length. */\nfunction prefixSimilarity(a: string, b: string): number {\n if (a === b) return 1;\n const max = Math.max(a.length, b.length);\n if (max === 0) return 1;\n let p = 0;\n const lim = Math.min(a.length, b.length);\n while (p < lim && a.charCodeAt(p) === b.charCodeAt(p)) p++;\n return p / max;\n}\n", "/**\n * Post-edit syntax validation for `edit` and `write`.\n *\n * After a mutation lands on disk, the new content is parsed (TS/JS via the\n * TypeScript compiler's parser, JSON via JSON.parse with a JSONC fallback)\n * and any parse errors are surfaced in the tool output. The edit is NOT\n * rolled back \u2014 reverting would desynchronize the model's picture of the\n * file \u2014 but the errors come back in the same turn so the model fixes the\n * breakage before the user ever opens a broken file.\n *\n * The check is purely syntactic (no type checking, no project program), so\n * it costs single-digit milliseconds per file. Semantic type errors remain\n * the `type-gate` plugin's job.\n */\nimport * as path from 'node:path';\n\nconst TS_LIKE = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs']);\n/** Skip pathologically large files \u2014 the model never writes these in one go. */\nconst MAX_CHECK_CHARS = 1_500_000;\n/** Cap reported errors so a mangled file doesn't flood the context. */\nconst MAX_ERRORS = 5;\n\ntype Ts = typeof import('@typescript/typescript6');\n\nlet tsLoad: Promise<Ts | null> | null = null;\n/** Lazy-load the TypeScript compiler (already a dependency via the codebase\n * indexer) so tools that never touch TS files don't pay its import cost. */\nfunction loadTypescript(): Promise<Ts | null> {\n tsLoad ??= import('@typescript/typescript6').then(\n (m) => ((m as unknown as { default?: Ts }).default ?? m) as Ts,\n () => null,\n );\n return tsLoad;\n}\n\nexport interface SyntaxCheckResult {\n /** Parse errors in the new content (capped at {@link MAX_ERRORS}). */\n errors: string[];\n /** True when the pre-edit content already failed to parse \u2014 the edit did\n * not introduce the breakage (though it didn't fix it either). */\n preExisting: boolean;\n}\n\n/**\n * Check `content` for syntax errors based on the file extension.\n * Returns `undefined` when the file type is not checkable (or the checker\n * is unavailable) and a result with an empty `errors` array when clean.\n * Pass `previousContent` to distinguish newly-introduced errors from\n * pre-existing ones.\n */\nexport async function checkSyntax(\n filePath: string,\n content: string,\n previousContent?: string,\n): Promise<SyntaxCheckResult | undefined> {\n const errors = await parseErrors(filePath, content);\n if (errors === undefined) return undefined;\n if (errors.length === 0) return { errors, preExisting: false };\n let preExisting = false;\n if (previousContent !== undefined) {\n const prevErrors = await parseErrors(filePath, previousContent);\n preExisting = prevErrors !== undefined && prevErrors.length > 0;\n }\n return { errors, preExisting };\n}\n\n/** Files that are conventionally JSONC (comments + trailing commas allowed). */\nfunction isJsoncFile(filePath: string): boolean {\n const base = path.basename(filePath).toLowerCase();\n if (base.endsWith('.jsonc')) return true;\n if (/^(tsconfig|jsconfig)([.-].*)?\\.json$/.test(base)) return true;\n const dir = path.basename(path.dirname(filePath)).toLowerCase();\n return dir === '.vscode';\n}\n\nasync function parseErrors(filePath: string, content: string): Promise<string[] | undefined> {\n if (content.length > MAX_CHECK_CHARS) return undefined;\n const ext = path.extname(filePath).toLowerCase();\n\n if (ext === '.json' || ext === '.jsonc') {\n try {\n JSON.parse(content);\n return [];\n } catch (err) {\n // Known-JSONC files (tsconfig.json, VS Code settings, *.jsonc) accept\n // comments and trailing commas \u2014 re-parse with the TS config parser\n // before declaring them broken. Plain .json stays strict: a trailing\n // comma there IS an error the model should fix.\n if (isJsoncFile(filePath)) {\n const ts = await loadTypescript();\n if (ts) {\n const jsonc = ts.parseConfigFileTextToJson(filePath, content);\n if (!jsonc.error) return [];\n return [formatDiag(ts, jsonc.error, content)];\n }\n }\n return [`JSON parse error: ${(err as Error).message}`];\n }\n }\n\n if (!TS_LIKE.has(ext)) return undefined;\n const ts = await loadTypescript();\n if (!ts) return undefined;\n\n const scriptKind =\n ext === '.tsx'\n ? ts.ScriptKind.TSX\n : ext === '.ts' || ext === '.mts' || ext === '.cts'\n ? ts.ScriptKind.TS\n : // Plain JS may legitimately contain JSX; the JSX grammar is a\n // superset for untyped code, so parsing .js/.jsx as JSX avoids\n // false positives on React files.\n ts.ScriptKind.JSX;\n\n const sourceFile = ts.createSourceFile(\n path.basename(filePath),\n content,\n ts.ScriptTarget.Latest,\n /* setParentNodes */ false,\n scriptKind,\n );\n // parseDiagnostics is not in the public .d.ts but has been the stable\n // home of the parser's syntactic diagnostics for a decade (the public\n // alternative, program.getSyntacticDiagnostics, needs a full Program).\n const diags =\n (sourceFile as unknown as {\n parseDiagnostics?: import('@typescript/typescript6').Diagnostic[];\n })\n .parseDiagnostics ?? [];\n return diags.slice(0, MAX_ERRORS).map((d) => formatDiag(ts, d, content, sourceFile));\n}\n\nfunction formatDiag(\n ts: Ts,\n diag: import('@typescript/typescript6').Diagnostic,\n content: string,\n sourceFile?: import('@typescript/typescript6').SourceFile,\n): string {\n const message = ts.flattenDiagnosticMessageText(diag.messageText, ' ');\n if (diag.start === undefined) return message;\n let line: number;\n if (sourceFile) {\n line = sourceFile.getLineAndCharacterOfPosition(diag.start).line + 1;\n } else {\n line = 1;\n for (let i = 0; i < diag.start && i < content.length; i++) {\n if (content.charCodeAt(i) === 0x0a) line++;\n }\n }\n return `line ${line}: ${message}`;\n}\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core/utils';\nimport type { Context } from '@wrongstack/core/agent';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` \u2014 the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm \u2192 yarn \u2192 npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root\u2192out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink \u2014 macOS `/var`\u2192`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n\u2026[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]\u2026\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// \u2500\u2500\u2500 Command-output normalization (token-saving) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) \u2014 never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only \u2014 it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `\u2026 \u27E8repeated ${run}\u00D7\u27E9`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends \u2014 the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n\u2026[truncated ${total - kept} bytes]\u2026\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI \u2192 collapse\n * carriage-return progress \u2192 trim trailing whitespace \u2192 collapse identical\n * consecutive lines \u2192 squeeze blank-line runs \u2192 head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines \u2192 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["line", "ts", "path", "stat"]
|
|
7
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"exec-kill-guard.d.ts","sourceRoot":"","sources":["../src/exec-kill-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAQH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAoM9B"}
|
package/dist/exec.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAUnD,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA+J1E;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CAAO,GAAG,IAAI,CAQpI;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAkBD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CAAO,GAAG,IAAI,CAOjG;AAED,8DAA8D;AAC9D,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAAC,MAAM,CAAC,CAErD;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,8EAA8E;AAC9E,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C;AA4GD,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;OASG;IACH,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAID,eAAO,MAAM,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CA2JhD,CAAC"}
|