@bastani/atomic 0.6.8 → 0.7.0-2
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/bin/atomic +65 -0
- package/package.json +17 -82
- package/postinstall.mjs +47 -0
- package/.agents/skills/ado-commit/SKILL.md +0 -94
- package/.agents/skills/ado-create-pr/SKILL.md +0 -211
- package/.agents/skills/advanced-evaluation/SKILL.md +0 -404
- package/.agents/skills/advanced-evaluation/references/bias-mitigation.md +0 -288
- package/.agents/skills/advanced-evaluation/references/evaluation-pipeline.md +0 -43
- package/.agents/skills/advanced-evaluation/references/implementation-patterns.md +0 -315
- package/.agents/skills/advanced-evaluation/references/metrics-guide.md +0 -331
- package/.agents/skills/advanced-evaluation/scripts/evaluation_example.py +0 -392
- package/.agents/skills/ast-grep/SKILL.md +0 -325
- package/.agents/skills/ast-grep/references/rule_reference.md +0 -297
- package/.agents/skills/bdi-mental-states/SKILL.md +0 -313
- package/.agents/skills/bdi-mental-states/references/bdi-ontology-core.md +0 -207
- package/.agents/skills/bdi-mental-states/references/framework-integration.md +0 -582
- package/.agents/skills/bdi-mental-states/references/rdf-examples.md +0 -315
- package/.agents/skills/bdi-mental-states/references/sparql-competency.md +0 -420
- package/.agents/skills/bun/SKILL.md +0 -233
- package/.agents/skills/context-compression/SKILL.md +0 -274
- package/.agents/skills/context-compression/references/evaluation-framework.md +0 -213
- package/.agents/skills/context-compression/scripts/compression_evaluator.py +0 -862
- package/.agents/skills/context-compression/tests/test_compression_evaluator.py +0 -56
- package/.agents/skills/context-degradation/SKILL.md +0 -208
- package/.agents/skills/context-degradation/references/patterns.md +0 -314
- package/.agents/skills/context-degradation/scripts/degradation_detector.py +0 -614
- package/.agents/skills/context-fundamentals/SKILL.md +0 -203
- package/.agents/skills/context-fundamentals/references/context-components.md +0 -283
- package/.agents/skills/context-fundamentals/scripts/context_manager.py +0 -533
- package/.agents/skills/context-optimization/SKILL.md +0 -197
- package/.agents/skills/context-optimization/references/optimization_techniques.md +0 -272
- package/.agents/skills/context-optimization/scripts/compaction.py +0 -562
- package/.agents/skills/create-spec/SKILL.md +0 -249
- package/.agents/skills/docx/LICENSE.txt +0 -30
- package/.agents/skills/docx/SKILL.md +0 -592
- package/.agents/skills/docx/scripts/__init__.py +0 -1
- package/.agents/skills/docx/scripts/accept_changes.py +0 -135
- package/.agents/skills/docx/scripts/comment.py +0 -318
- package/.agents/skills/docx/scripts/office/helpers/__init__.py +0 -0
- package/.agents/skills/docx/scripts/office/helpers/merge_runs.py +0 -199
- package/.agents/skills/docx/scripts/office/helpers/simplify_redlines.py +0 -197
- package/.agents/skills/docx/scripts/office/pack.py +0 -159
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/.agents/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/.agents/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/.agents/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/.agents/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/.agents/skills/docx/scripts/office/schemas/mce/mc.xsd +0 -75
- package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
- package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
- package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
- package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/.agents/skills/docx/scripts/office/soffice.py +0 -183
- package/.agents/skills/docx/scripts/office/unpack.py +0 -132
- package/.agents/skills/docx/scripts/office/validate.py +0 -111
- package/.agents/skills/docx/scripts/office/validators/__init__.py +0 -15
- package/.agents/skills/docx/scripts/office/validators/base.py +0 -847
- package/.agents/skills/docx/scripts/office/validators/docx.py +0 -446
- package/.agents/skills/docx/scripts/office/validators/pptx.py +0 -275
- package/.agents/skills/docx/scripts/office/validators/redlining.py +0 -247
- package/.agents/skills/docx/scripts/templates/comments.xml +0 -3
- package/.agents/skills/docx/scripts/templates/commentsExtended.xml +0 -3
- package/.agents/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
- package/.agents/skills/docx/scripts/templates/commentsIds.xml +0 -3
- package/.agents/skills/docx/scripts/templates/people.xml +0 -3
- package/.agents/skills/evaluation/SKILL.md +0 -253
- package/.agents/skills/evaluation/references/metrics.md +0 -339
- package/.agents/skills/evaluation/scripts/evaluator.py +0 -627
- package/.agents/skills/explain-code/SKILL.md +0 -232
- package/.agents/skills/filesystem-context/SKILL.md +0 -289
- package/.agents/skills/filesystem-context/references/implementation-patterns.md +0 -549
- package/.agents/skills/filesystem-context/scripts/filesystem_context.py +0 -425
- package/.agents/skills/find-skills/SKILL.md +0 -144
- package/.agents/skills/gh-commit/SKILL.md +0 -245
- package/.agents/skills/gh-create-pr/SKILL.md +0 -95
- package/.agents/skills/hosted-agents/SKILL.md +0 -262
- package/.agents/skills/hosted-agents/references/infrastructure-patterns.md +0 -700
- package/.agents/skills/hosted-agents/scripts/sandbox_manager.py +0 -590
- package/.agents/skills/impeccable/SKILL.md +0 -178
- package/.agents/skills/impeccable/agents/openai.yaml +0 -4
- package/.agents/skills/impeccable/reference/adapt.md +0 -190
- package/.agents/skills/impeccable/reference/animate.md +0 -175
- package/.agents/skills/impeccable/reference/audit.md +0 -134
- package/.agents/skills/impeccable/reference/bolder.md +0 -113
- package/.agents/skills/impeccable/reference/brand.md +0 -114
- package/.agents/skills/impeccable/reference/clarify.md +0 -174
- package/.agents/skills/impeccable/reference/cognitive-load.md +0 -106
- package/.agents/skills/impeccable/reference/color-and-contrast.md +0 -105
- package/.agents/skills/impeccable/reference/colorize.md +0 -154
- package/.agents/skills/impeccable/reference/craft.md +0 -193
- package/.agents/skills/impeccable/reference/critique.md +0 -213
- package/.agents/skills/impeccable/reference/delight.md +0 -302
- package/.agents/skills/impeccable/reference/distill.md +0 -111
- package/.agents/skills/impeccable/reference/document.md +0 -427
- package/.agents/skills/impeccable/reference/extract.md +0 -70
- package/.agents/skills/impeccable/reference/harden.md +0 -347
- package/.agents/skills/impeccable/reference/heuristics-scoring.md +0 -234
- package/.agents/skills/impeccable/reference/interaction-design.md +0 -195
- package/.agents/skills/impeccable/reference/layout.md +0 -141
- package/.agents/skills/impeccable/reference/live.md +0 -594
- package/.agents/skills/impeccable/reference/motion-design.md +0 -109
- package/.agents/skills/impeccable/reference/onboard.md +0 -234
- package/.agents/skills/impeccable/reference/optimize.md +0 -258
- package/.agents/skills/impeccable/reference/overdrive.md +0 -130
- package/.agents/skills/impeccable/reference/personas.md +0 -178
- package/.agents/skills/impeccable/reference/polish.md +0 -232
- package/.agents/skills/impeccable/reference/product.md +0 -62
- package/.agents/skills/impeccable/reference/quieter.md +0 -99
- package/.agents/skills/impeccable/reference/responsive-design.md +0 -114
- package/.agents/skills/impeccable/reference/shape.md +0 -151
- package/.agents/skills/impeccable/reference/spatial-design.md +0 -100
- package/.agents/skills/impeccable/reference/teach.md +0 -156
- package/.agents/skills/impeccable/reference/typeset.md +0 -124
- package/.agents/skills/impeccable/reference/typography.md +0 -159
- package/.agents/skills/impeccable/reference/ux-writing.md +0 -107
- package/.agents/skills/impeccable/scripts/cleanup-deprecated.mjs +0 -284
- package/.agents/skills/impeccable/scripts/command-metadata.json +0 -94
- package/.agents/skills/impeccable/scripts/design-parser.mjs +0 -820
- package/.agents/skills/impeccable/scripts/detect-csp.mjs +0 -198
- package/.agents/skills/impeccable/scripts/is-generated.mjs +0 -69
- package/.agents/skills/impeccable/scripts/live-accept.mjs +0 -595
- package/.agents/skills/impeccable/scripts/live-browser.js +0 -4781
- package/.agents/skills/impeccable/scripts/live-inject.mjs +0 -445
- package/.agents/skills/impeccable/scripts/live-poll.mjs +0 -186
- package/.agents/skills/impeccable/scripts/live-server.mjs +0 -694
- package/.agents/skills/impeccable/scripts/live-wrap.mjs +0 -571
- package/.agents/skills/impeccable/scripts/live.mjs +0 -247
- package/.agents/skills/impeccable/scripts/load-context.mjs +0 -141
- package/.agents/skills/impeccable/scripts/modern-screenshot.umd.js +0 -14
- package/.agents/skills/impeccable/scripts/pin.mjs +0 -214
- package/.agents/skills/init/SKILL.md +0 -140
- package/.agents/skills/liteparse/SKILL.md +0 -223
- package/.agents/skills/memory-systems/SKILL.md +0 -221
- package/.agents/skills/memory-systems/references/implementation.md +0 -551
- package/.agents/skills/memory-systems/scripts/memory_store.py +0 -616
- package/.agents/skills/multi-agent-patterns/SKILL.md +0 -259
- package/.agents/skills/multi-agent-patterns/references/frameworks.md +0 -433
- package/.agents/skills/multi-agent-patterns/scripts/coordination.py +0 -613
- package/.agents/skills/opentui/SKILL.md +0 -202
- package/.agents/skills/opentui/references/animation/REFERENCE.md +0 -431
- package/.agents/skills/opentui/references/components/REFERENCE.md +0 -144
- package/.agents/skills/opentui/references/components/code-diff.md +0 -672
- package/.agents/skills/opentui/references/components/containers.md +0 -417
- package/.agents/skills/opentui/references/components/inputs.md +0 -531
- package/.agents/skills/opentui/references/components/text-display.md +0 -386
- package/.agents/skills/opentui/references/core/REFERENCE.md +0 -145
- package/.agents/skills/opentui/references/core/api.md +0 -543
- package/.agents/skills/opentui/references/core/configuration.md +0 -168
- package/.agents/skills/opentui/references/core/gotchas.md +0 -393
- package/.agents/skills/opentui/references/core/patterns.md +0 -449
- package/.agents/skills/opentui/references/keyboard/REFERENCE.md +0 -617
- package/.agents/skills/opentui/references/layout/REFERENCE.md +0 -337
- package/.agents/skills/opentui/references/layout/patterns.md +0 -444
- package/.agents/skills/opentui/references/react/REFERENCE.md +0 -174
- package/.agents/skills/opentui/references/react/api.md +0 -436
- package/.agents/skills/opentui/references/react/configuration.md +0 -302
- package/.agents/skills/opentui/references/react/gotchas.md +0 -443
- package/.agents/skills/opentui/references/react/patterns.md +0 -501
- package/.agents/skills/opentui/references/solid/REFERENCE.md +0 -201
- package/.agents/skills/opentui/references/solid/api.md +0 -564
- package/.agents/skills/opentui/references/solid/configuration.md +0 -316
- package/.agents/skills/opentui/references/solid/gotchas.md +0 -427
- package/.agents/skills/opentui/references/solid/patterns.md +0 -560
- package/.agents/skills/opentui/references/testing/REFERENCE.md +0 -614
- package/.agents/skills/pdf/LICENSE.txt +0 -30
- package/.agents/skills/pdf/SKILL.md +0 -316
- package/.agents/skills/pdf/forms.md +0 -294
- package/.agents/skills/pdf/reference.md +0 -612
- package/.agents/skills/pdf/scripts/check_bounding_boxes.py +0 -65
- package/.agents/skills/pdf/scripts/check_fillable_fields.py +0 -11
- package/.agents/skills/pdf/scripts/convert_pdf_to_images.py +0 -33
- package/.agents/skills/pdf/scripts/create_validation_image.py +0 -37
- package/.agents/skills/pdf/scripts/extract_form_field_info.py +0 -122
- package/.agents/skills/pdf/scripts/extract_form_structure.py +0 -115
- package/.agents/skills/pdf/scripts/fill_fillable_fields.py +0 -98
- package/.agents/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -107
- package/.agents/skills/playwright-cli/SKILL.md +0 -390
- package/.agents/skills/playwright-cli/references/element-attributes.md +0 -23
- package/.agents/skills/playwright-cli/references/playwright-tests.md +0 -39
- package/.agents/skills/playwright-cli/references/request-mocking.md +0 -87
- package/.agents/skills/playwright-cli/references/running-code.md +0 -241
- package/.agents/skills/playwright-cli/references/session-management.md +0 -225
- package/.agents/skills/playwright-cli/references/spec-driven-testing.md +0 -305
- package/.agents/skills/playwright-cli/references/storage-state.md +0 -275
- package/.agents/skills/playwright-cli/references/test-generation.md +0 -134
- package/.agents/skills/playwright-cli/references/tracing.md +0 -139
- package/.agents/skills/playwright-cli/references/video-recording.md +0 -143
- package/.agents/skills/pptx/LICENSE.txt +0 -30
- package/.agents/skills/pptx/SKILL.md +0 -234
- package/.agents/skills/pptx/editing.md +0 -205
- package/.agents/skills/pptx/pptxgenjs.md +0 -420
- package/.agents/skills/pptx/scripts/__init__.py +0 -0
- package/.agents/skills/pptx/scripts/add_slide.py +0 -195
- package/.agents/skills/pptx/scripts/clean.py +0 -286
- package/.agents/skills/pptx/scripts/office/helpers/__init__.py +0 -0
- package/.agents/skills/pptx/scripts/office/helpers/merge_runs.py +0 -199
- package/.agents/skills/pptx/scripts/office/helpers/simplify_redlines.py +0 -197
- package/.agents/skills/pptx/scripts/office/pack.py +0 -159
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/.agents/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/.agents/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/.agents/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/.agents/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/.agents/skills/pptx/scripts/office/schemas/mce/mc.xsd +0 -75
- package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
- package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
- package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
- package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/.agents/skills/pptx/scripts/office/soffice.py +0 -183
- package/.agents/skills/pptx/scripts/office/unpack.py +0 -132
- package/.agents/skills/pptx/scripts/office/validate.py +0 -111
- package/.agents/skills/pptx/scripts/office/validators/__init__.py +0 -15
- package/.agents/skills/pptx/scripts/office/validators/base.py +0 -847
- package/.agents/skills/pptx/scripts/office/validators/docx.py +0 -446
- package/.agents/skills/pptx/scripts/office/validators/pptx.py +0 -275
- package/.agents/skills/pptx/scripts/office/validators/redlining.py +0 -247
- package/.agents/skills/pptx/scripts/thumbnail.py +0 -289
- package/.agents/skills/project-development/SKILL.md +0 -293
- package/.agents/skills/project-development/references/case-studies.md +0 -388
- package/.agents/skills/project-development/references/pipeline-patterns.md +0 -610
- package/.agents/skills/project-development/scripts/pipeline_template.py +0 -796
- package/.agents/skills/prompt-engineer/SKILL.md +0 -265
- package/.agents/skills/prompt-engineer/references/advanced_patterns.md +0 -271
- package/.agents/skills/prompt-engineer/references/core_prompting.md +0 -137
- package/.agents/skills/prompt-engineer/references/quality_improvement.md +0 -193
- package/.agents/skills/research-codebase/SKILL.md +0 -229
- package/.agents/skills/ripgrep/SKILL.md +0 -384
- package/.agents/skills/skill-creator/LICENSE.txt +0 -202
- package/.agents/skills/skill-creator/SKILL.md +0 -487
- package/.agents/skills/skill-creator/agents/analyzer.md +0 -274
- package/.agents/skills/skill-creator/agents/comparator.md +0 -202
- package/.agents/skills/skill-creator/agents/grader.md +0 -223
- package/.agents/skills/skill-creator/assets/eval_review.html +0 -146
- package/.agents/skills/skill-creator/eval-viewer/generate_review.py +0 -471
- package/.agents/skills/skill-creator/eval-viewer/viewer.html +0 -1325
- package/.agents/skills/skill-creator/references/schemas.md +0 -430
- package/.agents/skills/skill-creator/scripts/__init__.py +0 -0
- package/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
- package/.agents/skills/skill-creator/scripts/generate_report.py +0 -326
- package/.agents/skills/skill-creator/scripts/improve_description.py +0 -247
- package/.agents/skills/skill-creator/scripts/package_skill.py +0 -136
- package/.agents/skills/skill-creator/scripts/quick_validate.py +0 -103
- package/.agents/skills/skill-creator/scripts/run_eval.py +0 -310
- package/.agents/skills/skill-creator/scripts/run_loop.py +0 -328
- package/.agents/skills/skill-creator/scripts/utils.py +0 -47
- package/.agents/skills/sl-commit/SKILL.md +0 -53
- package/.agents/skills/sl-submit-diff/SKILL.md +0 -57
- package/.agents/skills/tdd/SKILL.md +0 -111
- package/.agents/skills/tdd/deep-modules.md +0 -33
- package/.agents/skills/tdd/interface-design.md +0 -31
- package/.agents/skills/tdd/mocking.md +0 -59
- package/.agents/skills/tdd/refactoring.md +0 -10
- package/.agents/skills/tdd/tests.md +0 -61
- package/.agents/skills/tool-design/SKILL.md +0 -273
- package/.agents/skills/tool-design/references/architectural_reduction.md +0 -210
- package/.agents/skills/tool-design/references/best_practices.md +0 -176
- package/.agents/skills/tool-design/scripts/description_generator.py +0 -528
- package/.agents/skills/typescript-advanced-types/SKILL.md +0 -720
- package/.agents/skills/typescript-expert/SKILL.md +0 -434
- package/.agents/skills/typescript-expert/references/tsconfig-strict.json +0 -92
- package/.agents/skills/typescript-expert/references/typescript-cheatsheet.md +0 -383
- package/.agents/skills/typescript-expert/references/utility-types.ts +0 -335
- package/.agents/skills/typescript-expert/scripts/ts_diagnostic.py +0 -203
- package/.agents/skills/typescript-react-reviewer/SKILL.md +0 -201
- package/.agents/skills/typescript-react-reviewer/references/antipatterns.md +0 -510
- package/.agents/skills/typescript-react-reviewer/references/checklist.md +0 -267
- package/.agents/skills/typescript-react-reviewer/references/react19-patterns.md +0 -305
- package/.agents/skills/workflow-creator/SKILL.md +0 -553
- package/.agents/skills/workflow-creator/references/agent-sessions.md +0 -891
- package/.agents/skills/workflow-creator/references/agent-setup-recipe.md +0 -266
- package/.agents/skills/workflow-creator/references/computation-and-validation.md +0 -201
- package/.agents/skills/workflow-creator/references/control-flow.md +0 -470
- package/.agents/skills/workflow-creator/references/failure-modes.md +0 -1014
- package/.agents/skills/workflow-creator/references/getting-started.md +0 -392
- package/.agents/skills/workflow-creator/references/registry-and-validation.md +0 -141
- package/.agents/skills/workflow-creator/references/running-workflows.md +0 -418
- package/.agents/skills/workflow-creator/references/session-config.md +0 -431
- package/.agents/skills/workflow-creator/references/state-and-data-flow.md +0 -356
- package/.agents/skills/workflow-creator/references/user-input.md +0 -234
- package/.agents/skills/workflow-creator/references/workflow-inputs.md +0 -392
- package/.agents/skills/xlsx/LICENSE.txt +0 -30
- package/.agents/skills/xlsx/SKILL.md +0 -294
- package/.agents/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
- package/.agents/skills/xlsx/scripts/office/helpers/merge_runs.py +0 -199
- package/.agents/skills/xlsx/scripts/office/helpers/simplify_redlines.py +0 -197
- package/.agents/skills/xlsx/scripts/office/pack.py +0 -159
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/.agents/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/.agents/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/.agents/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/.agents/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/.agents/skills/xlsx/scripts/office/schemas/mce/mc.xsd +0 -75
- package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
- package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
- package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
- package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/.agents/skills/xlsx/scripts/office/soffice.py +0 -183
- package/.agents/skills/xlsx/scripts/office/unpack.py +0 -132
- package/.agents/skills/xlsx/scripts/office/validate.py +0 -111
- package/.agents/skills/xlsx/scripts/office/validators/__init__.py +0 -15
- package/.agents/skills/xlsx/scripts/office/validators/base.py +0 -847
- package/.agents/skills/xlsx/scripts/office/validators/docx.py +0 -446
- package/.agents/skills/xlsx/scripts/office/validators/pptx.py +0 -275
- package/.agents/skills/xlsx/scripts/office/validators/redlining.py +0 -247
- package/.agents/skills/xlsx/scripts/recalc.py +0 -184
- package/.claude/agents/code-simplifier.md +0 -52
- package/.claude/agents/codebase-analyzer.md +0 -166
- package/.claude/agents/codebase-locator.md +0 -122
- package/.claude/agents/codebase-online-researcher.md +0 -148
- package/.claude/agents/codebase-pattern-finder.md +0 -247
- package/.claude/agents/codebase-research-analyzer.md +0 -179
- package/.claude/agents/codebase-research-locator.md +0 -145
- package/.claude/agents/debugger.md +0 -91
- package/.claude/agents/orchestrator.md +0 -19
- package/.claude/agents/planner.md +0 -295
- package/.claude/agents/reviewer.md +0 -98
- package/.claude/agents/worker.md +0 -165
- package/.claude/settings.json +0 -27
- package/.github/agents/code-simplifier.md +0 -52
- package/.github/agents/codebase-analyzer.md +0 -166
- package/.github/agents/codebase-locator.md +0 -122
- package/.github/agents/codebase-online-researcher.md +0 -146
- package/.github/agents/codebase-pattern-finder.md +0 -247
- package/.github/agents/codebase-research-analyzer.md +0 -179
- package/.github/agents/codebase-research-locator.md +0 -145
- package/.github/agents/debugger.md +0 -98
- package/.github/agents/orchestrator.md +0 -27
- package/.github/agents/planner.md +0 -305
- package/.github/agents/reviewer.md +0 -95
- package/.github/agents/worker.md +0 -237
- package/.github/lsp.json +0 -93
- package/.mcp.json +0 -20
- package/.opencode/agents/code-simplifier.md +0 -62
- package/.opencode/agents/codebase-analyzer.md +0 -171
- package/.opencode/agents/codebase-locator.md +0 -127
- package/.opencode/agents/codebase-online-researcher.md +0 -152
- package/.opencode/agents/codebase-pattern-finder.md +0 -252
- package/.opencode/agents/codebase-research-analyzer.md +0 -183
- package/.opencode/agents/codebase-research-locator.md +0 -149
- package/.opencode/agents/debugger.md +0 -99
- package/.opencode/agents/orchestrator.md +0 -27
- package/.opencode/agents/planner.md +0 -309
- package/.opencode/agents/reviewer.md +0 -103
- package/.opencode/agents/worker.md +0 -165
- package/.opencode/opencode.json +0 -25
- package/README.md +0 -1624
- package/assets/settings.schema.json +0 -51
- package/dist/commands/cli/claude-inflight-hook.d.ts +0 -100
- package/dist/commands/cli/claude-inflight-hook.d.ts.map +0 -1
- package/dist/commands/cli/claude-stop-hook.d.ts +0 -80
- package/dist/commands/cli/claude-stop-hook.d.ts.map +0 -1
- package/dist/lib/atomic-temp.d.ts +0 -8
- package/dist/lib/atomic-temp.d.ts.map +0 -1
- package/dist/lib/path-root-guard.d.ts +0 -4
- package/dist/lib/path-root-guard.d.ts.map +0 -1
- package/dist/lib/spawn.d.ts +0 -102
- package/dist/lib/spawn.d.ts.map +0 -1
- package/dist/lib/terminal-env.d.ts +0 -9
- package/dist/lib/terminal-env.d.ts.map +0 -1
- package/dist/sdk/components/attached-statusline.d.ts +0 -26
- package/dist/sdk/components/attached-statusline.d.ts.map +0 -1
- package/dist/sdk/components/color-utils.d.ts +0 -4
- package/dist/sdk/components/color-utils.d.ts.map +0 -1
- package/dist/sdk/components/compact-switcher.d.ts +0 -10
- package/dist/sdk/components/compact-switcher.d.ts.map +0 -1
- package/dist/sdk/components/connectors.d.ts +0 -16
- package/dist/sdk/components/connectors.d.ts.map +0 -1
- package/dist/sdk/components/edge.d.ts +0 -4
- package/dist/sdk/components/edge.d.ts.map +0 -1
- package/dist/sdk/components/error-boundary.d.ts +0 -23
- package/dist/sdk/components/error-boundary.d.ts.map +0 -1
- package/dist/sdk/components/graph-theme.d.ts +0 -18
- package/dist/sdk/components/graph-theme.d.ts.map +0 -1
- package/dist/sdk/components/header.d.ts +0 -3
- package/dist/sdk/components/header.d.ts.map +0 -1
- package/dist/sdk/components/hooks.d.ts +0 -15
- package/dist/sdk/components/hooks.d.ts.map +0 -1
- package/dist/sdk/components/layout.d.ts +0 -27
- package/dist/sdk/components/layout.d.ts.map +0 -1
- package/dist/sdk/components/node-card.d.ts +0 -10
- package/dist/sdk/components/node-card.d.ts.map +0 -1
- package/dist/sdk/components/orchestrator-panel-contexts.d.ts +0 -16
- package/dist/sdk/components/orchestrator-panel-contexts.d.ts.map +0 -1
- package/dist/sdk/components/orchestrator-panel-store.d.ts +0 -52
- package/dist/sdk/components/orchestrator-panel-store.d.ts.map +0 -1
- package/dist/sdk/components/orchestrator-panel-types.d.ts +0 -18
- package/dist/sdk/components/orchestrator-panel-types.d.ts.map +0 -1
- package/dist/sdk/components/orchestrator-panel.d.ts +0 -86
- package/dist/sdk/components/orchestrator-panel.d.ts.map +0 -1
- package/dist/sdk/components/renderer-background.d.ts +0 -9
- package/dist/sdk/components/renderer-background.d.ts.map +0 -1
- package/dist/sdk/components/session-graph-panel.d.ts +0 -7
- package/dist/sdk/components/session-graph-panel.d.ts.map +0 -1
- package/dist/sdk/components/status-helpers.d.ts +0 -6
- package/dist/sdk/components/status-helpers.d.ts.map +0 -1
- package/dist/sdk/components/statusline.d.ts +0 -5
- package/dist/sdk/components/statusline.d.ts.map +0 -1
- package/dist/sdk/components/tui-diagnostics.d.ts +0 -56
- package/dist/sdk/components/tui-diagnostics.d.ts.map +0 -1
- package/dist/sdk/components/workflow-picker-panel.d.ts +0 -126
- package/dist/sdk/components/workflow-picker-panel.d.ts.map +0 -1
- package/dist/sdk/define-workflow.d.ts +0 -107
- package/dist/sdk/define-workflow.d.ts.map +0 -1
- package/dist/sdk/errors.d.ts +0 -46
- package/dist/sdk/errors.d.ts.map +0 -1
- package/dist/sdk/index.d.ts +0 -26
- package/dist/sdk/index.d.ts.map +0 -1
- package/dist/sdk/primitives/inputs.d.ts +0 -36
- package/dist/sdk/primitives/inputs.d.ts.map +0 -1
- package/dist/sdk/primitives/metadata.d.ts +0 -40
- package/dist/sdk/primitives/metadata.d.ts.map +0 -1
- package/dist/sdk/primitives/run.d.ts +0 -57
- package/dist/sdk/primitives/run.d.ts.map +0 -1
- package/dist/sdk/primitives/sessions.d.ts +0 -128
- package/dist/sdk/primitives/sessions.d.ts.map +0 -1
- package/dist/sdk/providers/claude.d.ts +0 -392
- package/dist/sdk/providers/claude.d.ts.map +0 -1
- package/dist/sdk/providers/copilot.d.ts +0 -55
- package/dist/sdk/providers/copilot.d.ts.map +0 -1
- package/dist/sdk/providers/opencode.d.ts +0 -27
- package/dist/sdk/providers/opencode.d.ts.map +0 -1
- package/dist/sdk/registry.d.ts +0 -27
- package/dist/sdk/registry.d.ts.map +0 -1
- package/dist/sdk/runtime/attached-footer.d.ts +0 -31
- package/dist/sdk/runtime/attached-footer.d.ts.map +0 -1
- package/dist/sdk/runtime/cc-debounce.d.ts +0 -29
- package/dist/sdk/runtime/cc-debounce.d.ts.map +0 -1
- package/dist/sdk/runtime/executor-env.d.ts +0 -20
- package/dist/sdk/runtime/executor-env.d.ts.map +0 -1
- package/dist/sdk/runtime/executor.d.ts +0 -265
- package/dist/sdk/runtime/executor.d.ts.map +0 -1
- package/dist/sdk/runtime/graph-inference.d.ts +0 -35
- package/dist/sdk/runtime/graph-inference.d.ts.map +0 -1
- package/dist/sdk/runtime/orchestrator-entry.d.ts +0 -26
- package/dist/sdk/runtime/orchestrator-entry.d.ts.map +0 -1
- package/dist/sdk/runtime/panel.d.ts +0 -9
- package/dist/sdk/runtime/panel.d.ts.map +0 -1
- package/dist/sdk/runtime/port-discovery.d.ts +0 -71
- package/dist/sdk/runtime/port-discovery.d.ts.map +0 -1
- package/dist/sdk/runtime/status-writer.d.ts +0 -101
- package/dist/sdk/runtime/status-writer.d.ts.map +0 -1
- package/dist/sdk/runtime/theme.d.ts +0 -33
- package/dist/sdk/runtime/theme.d.ts.map +0 -1
- package/dist/sdk/runtime/tmux.d.ts +0 -307
- package/dist/sdk/runtime/tmux.d.ts.map +0 -1
- package/dist/sdk/runtime/version-compat.d.ts +0 -28
- package/dist/sdk/runtime/version-compat.d.ts.map +0 -1
- package/dist/sdk/types.d.ts +0 -435
- package/dist/sdk/types.d.ts.map +0 -1
- package/dist/sdk/worker-shared.d.ts +0 -42
- package/dist/sdk/worker-shared.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/claude/index.d.ts +0 -81
- package/dist/sdk/workflows/builtin/deep-research-codebase/claude/index.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/copilot/index.d.ts +0 -37
- package/dist/sdk/workflows/builtin/deep-research-codebase/copilot/index.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/batching.d.ts +0 -43
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/batching.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.d.ts +0 -14
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.d.ts +0 -136
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scout.d.ts +0 -58
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scout.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scratch.d.ts +0 -43
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scratch.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts +0 -37
- package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/claude/index.d.ts +0 -68
- package/dist/sdk/workflows/builtin/open-claude-design/claude/index.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/copilot/index.d.ts +0 -56
- package/dist/sdk/workflows/builtin/open-claude-design/copilot/index.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/constants.d.ts +0 -72
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/constants.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/design-system.d.ts +0 -46
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/design-system.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/export.d.ts +0 -32
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/export.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/import.d.ts +0 -33
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/import.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/prompts.d.ts +0 -106
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/prompts.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/scan.d.ts +0 -50
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/scan.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/validation.d.ts +0 -12
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/validation.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/open-claude-design/opencode/index.d.ts +0 -58
- package/dist/sdk/workflows/builtin/open-claude-design/opencode/index.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts +0 -37
- package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/ralph/copilot/index.d.ts +0 -34
- package/dist/sdk/workflows/builtin/ralph/copilot/index.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/ralph/helpers/copilot-reviewer.d.ts +0 -25
- package/dist/sdk/workflows/builtin/ralph/helpers/copilot-reviewer.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/ralph/helpers/git.d.ts +0 -69
- package/dist/sdk/workflows/builtin/ralph/helpers/git.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts +0 -266
- package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/ralph/helpers/review.d.ts +0 -24
- package/dist/sdk/workflows/builtin/ralph/helpers/review.d.ts.map +0 -1
- package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts +0 -33
- package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts.map +0 -1
- package/dist/sdk/workflows/index.d.ts +0 -32
- package/dist/sdk/workflows/index.d.ts.map +0 -1
- package/dist/services/config/additional-instructions.d.ts +0 -67
- package/dist/services/config/additional-instructions.d.ts.map +0 -1
- package/dist/services/config/atomic-config.d.ts +0 -42
- package/dist/services/config/atomic-config.d.ts.map +0 -1
- package/dist/services/config/definitions.d.ts +0 -52
- package/dist/services/config/definitions.d.ts.map +0 -1
- package/dist/services/config/index.d.ts +0 -7
- package/dist/services/config/index.d.ts.map +0 -1
- package/dist/services/config/scm-sync.d.ts +0 -37
- package/dist/services/config/scm-sync.d.ts.map +0 -1
- package/dist/services/config/settings-schema.d.ts +0 -2
- package/dist/services/config/settings-schema.d.ts.map +0 -1
- package/dist/services/system/copy.d.ts +0 -84
- package/dist/services/system/copy.d.ts.map +0 -1
- package/dist/services/system/detect.d.ts +0 -75
- package/dist/services/system/detect.d.ts.map +0 -1
- package/dist/theme/colors.d.ts +0 -35
- package/dist/theme/colors.d.ts.map +0 -1
- package/src/cli.ts +0 -397
- package/src/commands/builtin-registry.ts +0 -37
- package/src/commands/cli/chat/index.test.ts +0 -252
- package/src/commands/cli/chat/index.ts +0 -430
- package/src/commands/cli/chat.ts +0 -8
- package/src/commands/cli/claude-ask-hook.test.ts +0 -128
- package/src/commands/cli/claude-ask-hook.ts +0 -84
- package/src/commands/cli/claude-inflight-hook.test.ts +0 -598
- package/src/commands/cli/claude-inflight-hook.ts +0 -359
- package/src/commands/cli/claude-session-start-hook.ts +0 -61
- package/src/commands/cli/claude-stop-hook.test.ts +0 -317
- package/src/commands/cli/claude-stop-hook.ts +0 -441
- package/src/commands/cli/completions.ts +0 -24
- package/src/commands/cli/config.ts +0 -80
- package/src/commands/cli/footer.tsx +0 -248
- package/src/commands/cli/init/index.ts +0 -41
- package/src/commands/cli/init/onboarding.ts +0 -61
- package/src/commands/cli/init.ts +0 -8
- package/src/commands/cli/management-commands.ts +0 -112
- package/src/commands/cli/session.test.ts +0 -830
- package/src/commands/cli/session.ts +0 -447
- package/src/commands/cli/workflow-command.test.ts +0 -618
- package/src/commands/cli/workflow-inputs.test.ts +0 -353
- package/src/commands/cli/workflow-inputs.ts +0 -266
- package/src/commands/cli/workflow-list.test.ts +0 -235
- package/src/commands/cli/workflow-list.ts +0 -0
- package/src/commands/cli/workflow-status.test.ts +0 -451
- package/src/commands/cli/workflow-status.ts +0 -330
- package/src/commands/cli/workflow.ts +0 -196
- package/src/completions/bash.ts +0 -102
- package/src/completions/fish.ts +0 -136
- package/src/completions/index.ts +0 -7
- package/src/completions/powershell.ts +0 -195
- package/src/completions/zsh.ts +0 -150
- package/src/lib/atomic-temp.test.ts +0 -86
- package/src/lib/atomic-temp.ts +0 -62
- package/src/lib/common-ignore.ts +0 -46
- package/src/lib/merge.ts +0 -103
- package/src/lib/path-root-guard.ts +0 -38
- package/src/lib/spawn.test.ts +0 -109
- package/src/lib/spawn.ts +0 -678
- package/src/lib/terminal-env.test.ts +0 -343
- package/src/lib/terminal-env.ts +0 -100
- package/src/scripts/bump-version.ts +0 -94
- package/src/scripts/bundle-configs.ts +0 -116
- package/src/scripts/clean-dist.test.ts +0 -53
- package/src/scripts/clean-dist.ts +0 -37
- package/src/scripts/constants-base.ts +0 -14
- package/src/scripts/constants.ts +0 -35
- package/src/sdk/components/attached-statusline.tsx +0 -86
- package/src/sdk/components/color-utils.ts +0 -20
- package/src/sdk/components/compact-switcher.tsx +0 -78
- package/src/sdk/components/connectors.test.ts +0 -707
- package/src/sdk/components/connectors.ts +0 -160
- package/src/sdk/components/edge.tsx +0 -13
- package/src/sdk/components/error-boundary.tsx +0 -38
- package/src/sdk/components/graph-theme.ts +0 -37
- package/src/sdk/components/header.tsx +0 -85
- package/src/sdk/components/hooks.ts +0 -21
- package/src/sdk/components/layout.test.ts +0 -1245
- package/src/sdk/components/layout.ts +0 -223
- package/src/sdk/components/node-card.tsx +0 -91
- package/src/sdk/components/orchestrator-panel-contexts.ts +0 -35
- package/src/sdk/components/orchestrator-panel-store.test.ts +0 -847
- package/src/sdk/components/orchestrator-panel-store.ts +0 -187
- package/src/sdk/components/orchestrator-panel-types.ts +0 -23
- package/src/sdk/components/orchestrator-panel.tsx +0 -262
- package/src/sdk/components/renderer-background.ts +0 -49
- package/src/sdk/components/session-graph-panel.tsx +0 -471
- package/src/sdk/components/status-helpers.ts +0 -33
- package/src/sdk/components/statusline.tsx +0 -68
- package/src/sdk/components/tui-diagnostics.ts +0 -273
- package/src/sdk/components/workflow-picker-panel.tsx +0 -1613
- package/src/sdk/define-workflow.test.ts +0 -354
- package/src/sdk/define-workflow.ts +0 -275
- package/src/sdk/errors.test.ts +0 -83
- package/src/sdk/errors.ts +0 -77
- package/src/sdk/index.test.ts +0 -92
- package/src/sdk/index.ts +0 -101
- package/src/sdk/primitives/inputs.ts +0 -48
- package/src/sdk/primitives/metadata.ts +0 -63
- package/src/sdk/primitives/run.ts +0 -81
- package/src/sdk/primitives/sessions.test.ts +0 -594
- package/src/sdk/primitives/sessions.ts +0 -328
- package/src/sdk/providers/claude.ts +0 -1450
- package/src/sdk/providers/copilot.test.ts +0 -365
- package/src/sdk/providers/copilot.ts +0 -185
- package/src/sdk/providers/headless-hil-policy.test.ts +0 -211
- package/src/sdk/providers/opencode.ts +0 -88
- package/src/sdk/registry.ts +0 -132
- package/src/sdk/runtime/attached-footer.ts +0 -155
- package/src/sdk/runtime/cc-debounce.ts +0 -104
- package/src/sdk/runtime/executor-env.ts +0 -45
- package/src/sdk/runtime/executor.test.ts +0 -1321
- package/src/sdk/runtime/executor.ts +0 -2136
- package/src/sdk/runtime/graph-inference.ts +0 -50
- package/src/sdk/runtime/orchestrator-entry.ts +0 -110
- package/src/sdk/runtime/panel.tsx +0 -9
- package/src/sdk/runtime/port-discovery.test.ts +0 -573
- package/src/sdk/runtime/port-discovery.ts +0 -496
- package/src/sdk/runtime/status-writer.test.ts +0 -245
- package/src/sdk/runtime/status-writer.ts +0 -201
- package/src/sdk/runtime/theme.ts +0 -71
- package/src/sdk/runtime/tmux.conf +0 -112
- package/src/sdk/runtime/tmux.ts +0 -785
- package/src/sdk/runtime/version-compat.ts +0 -68
- package/src/sdk/types.ts +0 -548
- package/src/sdk/worker-shared.test.ts +0 -163
- package/src/sdk/worker-shared.ts +0 -155
- package/src/sdk/workflows/builtin/deep-research-codebase/claude/index.ts +0 -569
- package/src/sdk/workflows/builtin/deep-research-codebase/copilot/index.ts +0 -481
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/batching.ts +0 -65
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.ts +0 -24
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/ignore-by-default.d.ts +0 -8
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.ts +0 -958
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/scout.ts +0 -505
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/scratch.ts +0 -115
- package/src/sdk/workflows/builtin/deep-research-codebase/opencode/index.ts +0 -530
- package/src/sdk/workflows/builtin/open-claude-design/claude/index.ts +0 -500
- package/src/sdk/workflows/builtin/open-claude-design/copilot/index.ts +0 -508
- package/src/sdk/workflows/builtin/open-claude-design/helpers/constants.ts +0 -159
- package/src/sdk/workflows/builtin/open-claude-design/helpers/design-system.ts +0 -88
- package/src/sdk/workflows/builtin/open-claude-design/helpers/export.ts +0 -193
- package/src/sdk/workflows/builtin/open-claude-design/helpers/import.ts +0 -52
- package/src/sdk/workflows/builtin/open-claude-design/helpers/prompts.ts +0 -1110
- package/src/sdk/workflows/builtin/open-claude-design/helpers/scan.ts +0 -117
- package/src/sdk/workflows/builtin/open-claude-design/helpers/validation.ts +0 -38
- package/src/sdk/workflows/builtin/open-claude-design/opencode/index.ts +0 -610
- package/src/sdk/workflows/builtin/ralph/claude/index.ts +0 -272
- package/src/sdk/workflows/builtin/ralph/copilot/index.ts +0 -298
- package/src/sdk/workflows/builtin/ralph/helpers/copilot-reviewer.ts +0 -105
- package/src/sdk/workflows/builtin/ralph/helpers/git.ts +0 -201
- package/src/sdk/workflows/builtin/ralph/helpers/prompts.ts +0 -1108
- package/src/sdk/workflows/builtin/ralph/helpers/review.ts +0 -33
- package/src/sdk/workflows/builtin/ralph/opencode/index.ts +0 -290
- package/src/sdk/workflows/index.ts +0 -116
- package/src/services/config/additional-instructions.ts +0 -273
- package/src/services/config/atomic-config.ts +0 -210
- package/src/services/config/atomic-global-config.ts +0 -348
- package/src/services/config/config-path.ts +0 -19
- package/src/services/config/definitions.ts +0 -125
- package/src/services/config/index.ts +0 -7
- package/src/services/config/scm-sync.ts +0 -185
- package/src/services/config/settings-schema.ts +0 -2
- package/src/services/config/settings.ts +0 -144
- package/src/services/system/agents.ts +0 -95
- package/src/services/system/auth.test.ts +0 -343
- package/src/services/system/auth.ts +0 -140
- package/src/services/system/auto-sync.ts +0 -128
- package/src/services/system/copy.ts +0 -392
- package/src/services/system/detect.ts +0 -161
- package/src/services/system/file-lock.ts +0 -289
- package/src/services/system/install-ui.ts +0 -296
- package/src/services/system/skills.ts +0 -58
- package/src/theme/colors.ts +0 -96
- package/src/theme/logo.ts +0 -123
- package/src/version.ts +0 -7
|
@@ -1,1450 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Claude Code query abstraction.
|
|
3
|
-
*
|
|
4
|
-
* Sends a prompt to an interactive Claude Code session running in a tmux pane
|
|
5
|
-
* using `tmux send-keys -l --` (literal text) + `C-m` (raw carriage return).
|
|
6
|
-
* Verifies delivery by polling `capture-pane` and retries if needed.
|
|
7
|
-
*
|
|
8
|
-
* This is NOT headless — Claude runs as a full interactive TUI in the pane.
|
|
9
|
-
* We're automating keyboard input and reading pane output.
|
|
10
|
-
*
|
|
11
|
-
* Reliability hardened from oh-my-codex's sendToWorker implementation:
|
|
12
|
-
* - Pre-send readiness wait with exponential backoff
|
|
13
|
-
* - CLI-specific submit plan (Claude: 1 C-m per round)
|
|
14
|
-
* - Per-round capture verification (6 rounds)
|
|
15
|
-
* - Adaptive retry with C-u clear + retype
|
|
16
|
-
* - Post-submit active-task detection
|
|
17
|
-
* - File-based idle detection via session JSONL watching
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
getSessionMessages,
|
|
22
|
-
query as sdkQuery,
|
|
23
|
-
type SessionMessage,
|
|
24
|
-
type SDKUserMessage,
|
|
25
|
-
type Options as SDKOptions,
|
|
26
|
-
} from "@anthropic-ai/claude-agent-sdk";
|
|
27
|
-
import { respawnPane } from "../runtime/tmux.ts";
|
|
28
|
-
import { escBash } from "../runtime/executor.ts";
|
|
29
|
-
import { watch, unlink, mkdir, writeFile } from "node:fs/promises";
|
|
30
|
-
import { existsSync, writeFileSync } from "node:fs";
|
|
31
|
-
import { join } from "node:path";
|
|
32
|
-
import { randomUUID } from "node:crypto";
|
|
33
|
-
import { claudeHookDirs } from "../../commands/cli/claude-stop-hook.ts";
|
|
34
|
-
import {
|
|
35
|
-
clearInflightTracking,
|
|
36
|
-
waitForInflightDrained,
|
|
37
|
-
} from "../../commands/cli/claude-inflight-hook.ts";
|
|
38
|
-
import { resolveAdditionalInstructionsContent } from "../../services/config/additional-instructions.ts";
|
|
39
|
-
import {
|
|
40
|
-
atomicContentTempPath,
|
|
41
|
-
atomicTempPath,
|
|
42
|
-
withAtomicTempEnv,
|
|
43
|
-
} from "../../lib/atomic-temp.ts";
|
|
44
|
-
|
|
45
|
-
// ---------------------------------------------------------------------------
|
|
46
|
-
// Session tracking — ensures createClaudeSession is called before claudeQuery
|
|
47
|
-
// ---------------------------------------------------------------------------
|
|
48
|
-
|
|
49
|
-
/** Per-pane state for Claude sessions. */
|
|
50
|
-
interface PaneState {
|
|
51
|
-
/**
|
|
52
|
-
* Claude Code's session ID. Pre-generated via `crypto.randomUUID()` in
|
|
53
|
-
* `createClaudeSession` and passed to `claude --session-id <UUID>` on the
|
|
54
|
-
* first query, so we know the JSONL filename without polling.
|
|
55
|
-
*/
|
|
56
|
-
claudeSessionId: string;
|
|
57
|
-
/** Whether the `claude` CLI has been spawned in this pane yet. */
|
|
58
|
-
claudeStarted: boolean;
|
|
59
|
-
/** CLI flags to pass to `claude` when it is spawned on the first query. */
|
|
60
|
-
chatFlags: string[];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const initializedPanes = new Map<string, PaneState>();
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Remove a pane from the initialized map and signal the currently-blocked
|
|
67
|
-
* Stop hook that the session is over, so Claude stops promptly instead of
|
|
68
|
-
* waiting out the hook's safety timeout.
|
|
69
|
-
*
|
|
70
|
-
* Called by the runtime when a Claude stage is being torn down. Idempotent.
|
|
71
|
-
*
|
|
72
|
-
* After writing the release marker, this waits for the per-session in-flight
|
|
73
|
-
* marker dir (`~/.atomic/claude-inflight/<session_id>/`) to drain. The
|
|
74
|
-
* marker dir is populated by the SubagentStart/Stop and TaskCreated/Completed
|
|
75
|
-
* hooks registered in {@link WORKFLOW_HOOK_SETTINGS}. This wait is the
|
|
76
|
-
* synchronization barrier that prevents the executor from advancing to the
|
|
77
|
-
* next stage while the previous stage's backgrounded subagents/tasks still
|
|
78
|
-
* hold FDs/PTYs on the atomic tmux server — the failure mode that surfaced
|
|
79
|
-
* intermittently as `tmux respawn-pane: fork failed: Device not configured`.
|
|
80
|
-
*
|
|
81
|
-
* The wait has its own bounded timeout (default 30 minutes) so a wedged
|
|
82
|
-
* subagent can't permanently block the workflow; the in-hook stale-sweep
|
|
83
|
-
* (~2 hours TTL) is the ultimate safety net.
|
|
84
|
-
*/
|
|
85
|
-
export async function clearClaudeSession(paneId: string): Promise<void> {
|
|
86
|
-
const state = initializedPanes.get(paneId);
|
|
87
|
-
if (state) {
|
|
88
|
-
try {
|
|
89
|
-
await releaseClaudeSession(state.claudeSessionId);
|
|
90
|
-
} catch {
|
|
91
|
-
// Best-effort — if release fails the hook will still exit on its
|
|
92
|
-
// own safety timeout.
|
|
93
|
-
}
|
|
94
|
-
// Wait for in-flight subagents/tasks to finish before letting the
|
|
95
|
-
// executor advance. Resolves immediately when the dir is empty/missing
|
|
96
|
-
// (the common case, including any stage that didn't spawn subagents).
|
|
97
|
-
try {
|
|
98
|
-
await waitForInflightDrained(state.claudeSessionId);
|
|
99
|
-
} catch {
|
|
100
|
-
// Best-effort — the wait swallows internal errors and resolves on
|
|
101
|
-
// timeout. A throw here would only happen on a path bug.
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
await unlinkAtomicPidFile(state.claudeSessionId);
|
|
105
|
-
} catch {
|
|
106
|
-
// Best-effort — stale pid file is inert; the next session writes a
|
|
107
|
-
// fresh one under its own UUID.
|
|
108
|
-
}
|
|
109
|
-
try {
|
|
110
|
-
await clearStaleReadyMarker(state.claudeSessionId);
|
|
111
|
-
} catch {
|
|
112
|
-
// Best-effort — stale ready marker is inert; the next session writes
|
|
113
|
-
// a fresh one under its own UUID and clears any prior leftover in
|
|
114
|
-
// `claudeQuery` before respawn.
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
await clearInflightTracking(state.claudeSessionId);
|
|
118
|
-
} catch {
|
|
119
|
-
// Best-effort — leftover marker files are reaped by the next session's
|
|
120
|
-
// stale-sweep, and the .session-roots/ entries are tiny.
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
initializedPanes.delete(paneId);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Default CLI flags passed to the `claude` command. */
|
|
127
|
-
const DEFAULT_CHAT_FLAGS = [
|
|
128
|
-
"--allow-dangerously-skip-permissions",
|
|
129
|
-
"--dangerously-skip-permissions",
|
|
130
|
-
];
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Build the shell command Claude Code runs from an injected workflow hook.
|
|
134
|
-
*
|
|
135
|
-
* - **Published install** (`import.meta.dir` under `node_modules`): resolve
|
|
136
|
-
* `atomic` via the user's PATH. That's the binary they installed, and
|
|
137
|
-
* relying on PATH is robust across shells and platforms.
|
|
138
|
-
* - **Dev** (source checkout): re-invoke THIS repo's `src/cli.ts` using the
|
|
139
|
-
* same Bun runtime that's executing us, so edits to the hook logic are
|
|
140
|
-
* picked up without rebuilding or re-linking. Mirrors the
|
|
141
|
-
* `spawnAttachedFooter` pattern in `src/sdk/runtime/executor.ts:293-303`.
|
|
142
|
-
*
|
|
143
|
-
* The dev-detection heuristic (`node_modules` in `import.meta.dir`) is the
|
|
144
|
-
* same one used by `src/services/system/auto-sync.ts:50`.
|
|
145
|
-
*/
|
|
146
|
-
function buildWorkflowHookCommand(subcommand: string, extraArgs: readonly string[] = []): string {
|
|
147
|
-
if (import.meta.dir.includes("node_modules")) {
|
|
148
|
-
return ["atomic", subcommand, ...extraArgs].join(" ");
|
|
149
|
-
}
|
|
150
|
-
const runtime = process.execPath;
|
|
151
|
-
const cliPath = join(import.meta.dir, "..", "..", "cli.ts");
|
|
152
|
-
if (process.platform === "win32") {
|
|
153
|
-
const script = [
|
|
154
|
-
quotePwshLiteral(runtime),
|
|
155
|
-
quotePwshLiteral(cliPath),
|
|
156
|
-
quotePwshLiteral(subcommand),
|
|
157
|
-
...extraArgs.map(quotePwshLiteral),
|
|
158
|
-
].join(" ");
|
|
159
|
-
const encoded = Buffer.from(`& ${script}`, "utf16le").toString("base64");
|
|
160
|
-
return `pwsh -NoProfile -EncodedCommand ${encoded}`;
|
|
161
|
-
}
|
|
162
|
-
return [
|
|
163
|
-
`"${escBash(runtime)}"`,
|
|
164
|
-
`"${escBash(cliPath)}"`,
|
|
165
|
-
subcommand,
|
|
166
|
-
...extraArgs,
|
|
167
|
-
].join(" ");
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function quotePwshLiteral(s: string): string {
|
|
171
|
-
return `'${s
|
|
172
|
-
.replace(/\x00/g, "")
|
|
173
|
-
.replace(/[\n\r]+/g, " ")
|
|
174
|
-
.replace(/'/g, "''")}'`;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Effectively-unbounded timeout (in seconds) for the Stop hook command.
|
|
179
|
-
*
|
|
180
|
-
* Claude Code's Stop hook process runs three phases sequentially — the
|
|
181
|
-
* initial Stop hooks, then TaskCompleted hooks (per in-progress task owned
|
|
182
|
-
* by the teammate), then TeammateIdle hooks — and the per-hook `timeout`
|
|
183
|
-
* applies to the entire lifecycle. Under Claude Code's default (10 min),
|
|
184
|
-
* a turn that leaves tasks in-progress (e.g. via the TaskList/TodoWrite
|
|
185
|
-
* tool) can blow the budget and get killed, which also severs our
|
|
186
|
-
* `_claude-stop-hook`'s queue/release poll and strands the workflow.
|
|
187
|
-
*
|
|
188
|
-
* ~24 days — the max safe `setTimeout` value (2^31 - 1 ms) expressed in
|
|
189
|
-
* seconds — removes the timeout in practical terms. `waitForIdle`'s
|
|
190
|
-
* marker-file watch still fires as soon as our initial hook writes the
|
|
191
|
-
* marker, so the workflow proceeds on real hook completion, not on timer
|
|
192
|
-
* expiry.
|
|
193
|
-
*/
|
|
194
|
-
const STOP_HOOK_TIMEOUT_SECONDS = 2_147_483;
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Effectively-unbounded ms ceiling for `waitForReadyMarker`. Mirrors
|
|
198
|
-
* {@link STOP_HOOK_TIMEOUT_SECONDS} but expressed in ms for `setTimeout`.
|
|
199
|
-
*
|
|
200
|
-
* The SessionStart hook fires well under a second on a working spawn, so in
|
|
201
|
-
* practice this timer never expires. It only protects against failure modes
|
|
202
|
-
* where the hook will never fire at all (claude binary missing, hook
|
|
203
|
-
* command not resolvable, settings JSON rejected), where a clear error
|
|
204
|
-
* beats a hung pane.
|
|
205
|
-
*/
|
|
206
|
-
const READY_HOOK_TIMEOUT_MS = 2_147_483_000;
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Inline settings injected via `claude --settings <json>` on every workflow
|
|
210
|
-
* spawn. Registers the workflow-owned hooks without relying on
|
|
211
|
-
* `.claude/settings.json` — so the hooks fire only for workflow-spawned
|
|
212
|
-
* Claude sessions, not when a user runs `claude` manually.
|
|
213
|
-
*
|
|
214
|
-
* Registered hooks:
|
|
215
|
-
* - `Stop`: deliver queued follow-up prompts via `{decision:"block"}` and
|
|
216
|
-
* write an idle-marker file that `waitForIdle` watches. `timeout` is
|
|
217
|
-
* set to {@link STOP_HOOK_TIMEOUT_SECONDS} so the hook survives long
|
|
218
|
-
* TaskCompleted/TeammateIdle phases — see the constant's docstring.
|
|
219
|
-
* - `PreToolUse` matched on `AskUserQuestion`: write
|
|
220
|
-
* `~/.atomic/claude-hil/<session_id>` so `watchHILMarker` can fire
|
|
221
|
-
* `onHIL(true)` — the node card flips to the blue "awaiting_input" pulse.
|
|
222
|
-
* - `PostToolUse` / `PostToolUseFailure` matched on `AskUserQuestion`:
|
|
223
|
-
* remove the HIL marker. Claude Code fires exactly one of these per
|
|
224
|
-
* tool invocation (PostToolUse on success, PostToolUseFailure in the
|
|
225
|
-
* catch path — see `src/services/tools/toolExecution.ts` in the CLI
|
|
226
|
-
* source), so registering the same command on both guarantees the
|
|
227
|
-
* marker clears regardless of which completion path the tool takes.
|
|
228
|
-
* - `SubagentStart` / `SubagentStop`: maintain a per-root-session marker
|
|
229
|
-
* dir under `~/.atomic/claude-inflight/<root>/` so the Stop hook and
|
|
230
|
-
* `clearClaudeSession` can both gate on subagent completion before
|
|
231
|
-
* letting the stage advance. Without this gate, a stage that spawned
|
|
232
|
-
* `run_in_background: true` subagents would tear down its pane while
|
|
233
|
-
* children still hold FDs/PTYs on the atomic tmux server, intermittently
|
|
234
|
-
* surfacing as `tmux respawn-pane: fork failed: Device not configured`
|
|
235
|
-
* when the next stage tried to spawn.
|
|
236
|
-
* - `TeammateIdle`: same gating applied at agent-team teammate idle.
|
|
237
|
-
* Unlike Stop, this fires when a teammate (potentially a different
|
|
238
|
-
* `session_id` from the stage's root) goes idle, so we route it to a
|
|
239
|
-
* focused `_claude-inflight-hook wait` mode that only awaits in-flight
|
|
240
|
-
* drain — no claude-stop marker write (that would confuse `waitForIdle`)
|
|
241
|
-
* and no queue/release polling (those are keyed on the stage's root).
|
|
242
|
-
*
|
|
243
|
-
* Built once at module load. Contains no single quotes (JSON syntax doesn't
|
|
244
|
-
* produce them and paths rarely do), so POSIX single-quoting at the spawn
|
|
245
|
-
* site is sufficient shell escaping.
|
|
246
|
-
*/
|
|
247
|
-
const WORKFLOW_HOOK_SETTINGS = JSON.stringify({
|
|
248
|
-
hooks: {
|
|
249
|
-
SessionStart: [
|
|
250
|
-
{
|
|
251
|
-
matcher: "startup",
|
|
252
|
-
hooks: [
|
|
253
|
-
{
|
|
254
|
-
type: "command",
|
|
255
|
-
command: buildWorkflowHookCommand("_claude-session-start-hook"),
|
|
256
|
-
},
|
|
257
|
-
],
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
Stop: [
|
|
261
|
-
{
|
|
262
|
-
hooks: [
|
|
263
|
-
{
|
|
264
|
-
type: "command",
|
|
265
|
-
command: buildWorkflowHookCommand("_claude-stop-hook"),
|
|
266
|
-
timeout: STOP_HOOK_TIMEOUT_SECONDS,
|
|
267
|
-
},
|
|
268
|
-
],
|
|
269
|
-
},
|
|
270
|
-
],
|
|
271
|
-
PreToolUse: [
|
|
272
|
-
{
|
|
273
|
-
matcher: "AskUserQuestion",
|
|
274
|
-
hooks: [
|
|
275
|
-
{
|
|
276
|
-
type: "command",
|
|
277
|
-
command: buildWorkflowHookCommand("_claude-ask-hook", ["enter"]),
|
|
278
|
-
},
|
|
279
|
-
],
|
|
280
|
-
},
|
|
281
|
-
],
|
|
282
|
-
PostToolUse: [
|
|
283
|
-
{
|
|
284
|
-
matcher: "AskUserQuestion",
|
|
285
|
-
hooks: [
|
|
286
|
-
{
|
|
287
|
-
type: "command",
|
|
288
|
-
command: buildWorkflowHookCommand("_claude-ask-hook", ["exit"]),
|
|
289
|
-
},
|
|
290
|
-
],
|
|
291
|
-
},
|
|
292
|
-
],
|
|
293
|
-
PostToolUseFailure: [
|
|
294
|
-
{
|
|
295
|
-
matcher: "AskUserQuestion",
|
|
296
|
-
hooks: [
|
|
297
|
-
{
|
|
298
|
-
type: "command",
|
|
299
|
-
command: buildWorkflowHookCommand("_claude-ask-hook", ["exit"]),
|
|
300
|
-
},
|
|
301
|
-
],
|
|
302
|
-
},
|
|
303
|
-
],
|
|
304
|
-
// SubagentStart/SubagentStop fire per Agent-tool dispatch (no matcher)
|
|
305
|
-
// and route to a single subcommand that touches/removes one marker file
|
|
306
|
-
// per `agent_id`. The handler is bulletproof — any error exits 0
|
|
307
|
-
// silently — so a hook failure can't kill the stage.
|
|
308
|
-
SubagentStart: [
|
|
309
|
-
{
|
|
310
|
-
hooks: [
|
|
311
|
-
{
|
|
312
|
-
type: "command",
|
|
313
|
-
command: buildWorkflowHookCommand("_claude-inflight-hook", ["start"]),
|
|
314
|
-
},
|
|
315
|
-
],
|
|
316
|
-
},
|
|
317
|
-
],
|
|
318
|
-
SubagentStop: [
|
|
319
|
-
{
|
|
320
|
-
hooks: [
|
|
321
|
-
{
|
|
322
|
-
type: "command",
|
|
323
|
-
command: buildWorkflowHookCommand("_claude-inflight-hook", ["stop"]),
|
|
324
|
-
},
|
|
325
|
-
],
|
|
326
|
-
},
|
|
327
|
-
],
|
|
328
|
-
// TeammateIdle gets a focused `wait` mode (gates on in-flight drain,
|
|
329
|
-
// nothing else) — see the WORKFLOW_HOOK_SETTINGS docstring for why this
|
|
330
|
-
// doesn't reuse the Stop hook handler. Timeout matches Stop's so the
|
|
331
|
-
// wait can run for as long as the workflow holds onto teammates.
|
|
332
|
-
TeammateIdle: [
|
|
333
|
-
{
|
|
334
|
-
hooks: [
|
|
335
|
-
{
|
|
336
|
-
type: "command",
|
|
337
|
-
command: buildWorkflowHookCommand("_claude-inflight-hook", ["wait"]),
|
|
338
|
-
timeout: STOP_HOOK_TIMEOUT_SECONDS,
|
|
339
|
-
},
|
|
340
|
-
],
|
|
341
|
-
},
|
|
342
|
-
],
|
|
343
|
-
},
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
// ---------------------------------------------------------------------------
|
|
347
|
-
// createClaudeSession
|
|
348
|
-
// ---------------------------------------------------------------------------
|
|
349
|
-
|
|
350
|
-
export interface ClaudeSessionOptions {
|
|
351
|
-
/** tmux pane ID where Claude should be started */
|
|
352
|
-
paneId: string;
|
|
353
|
-
/** CLI flags to pass to the `claude` command (default: ["--allow-dangerously-skip-permissions", "--dangerously-skip-permissions"]) */
|
|
354
|
-
chatFlags?: string[];
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Initialize per-pane Claude state. Does NOT spawn the `claude` CLI — the
|
|
359
|
-
* pane is left as a bare shell. The CLI is spawned lazily on the first
|
|
360
|
-
* `claudeQuery()` call, with the prompt baked into the spawn command:
|
|
361
|
-
*
|
|
362
|
-
* claude [chatFlags] --session-id <UUID> 'Read the prompt in <tmpfile>'
|
|
363
|
-
*
|
|
364
|
-
* Pre-generating the session UUID here lets the first query pass it to the
|
|
365
|
-
* CLI, so we know the JSONL filename up front and can skip discovery polling.
|
|
366
|
-
*
|
|
367
|
-
* Must be called before any `claudeQuery()` calls targeting the same pane.
|
|
368
|
-
*
|
|
369
|
-
* @example
|
|
370
|
-
* ```typescript
|
|
371
|
-
* import { createClaudeSession, claudeQuery } from "@bastani/atomic/workflows";
|
|
372
|
-
*
|
|
373
|
-
* await createClaudeSession({ paneId: ctx.paneId });
|
|
374
|
-
* await claudeQuery({ paneId: ctx.paneId, prompt: "Describe this project" });
|
|
375
|
-
* ```
|
|
376
|
-
*
|
|
377
|
-
* @example
|
|
378
|
-
* ```typescript
|
|
379
|
-
* // With custom flags
|
|
380
|
-
* await createClaudeSession({
|
|
381
|
-
* paneId: ctx.paneId,
|
|
382
|
-
* chatFlags: ["--model", "opus", "--dangerously-skip-permissions"],
|
|
383
|
-
* });
|
|
384
|
-
* ```
|
|
385
|
-
*/
|
|
386
|
-
export async function createClaudeSession(options: ClaudeSessionOptions): Promise<string> {
|
|
387
|
-
const { paneId, chatFlags = DEFAULT_CHAT_FLAGS } = options;
|
|
388
|
-
|
|
389
|
-
const claudeSessionId = randomUUID();
|
|
390
|
-
initializedPanes.set(paneId, {
|
|
391
|
-
claudeSessionId,
|
|
392
|
-
claudeStarted: false,
|
|
393
|
-
chatFlags,
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
// Write our PID so the Stop hook can detect an orphaned session if we
|
|
397
|
-
// crash/get SIGKILL'd without running teardown. Best-effort; failures just
|
|
398
|
-
// mean the hook falls back to waiting out Claude's own hook timeout.
|
|
399
|
-
await writeAtomicPidFile(claudeSessionId);
|
|
400
|
-
|
|
401
|
-
return claudeSessionId;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Build the short, single-line natural-language prompt we send to Claude
|
|
406
|
-
* (either as spawn argv or as a follow-up message). Claude's first action
|
|
407
|
-
* is then a Read tool call against `promptFile` — which sidesteps shell
|
|
408
|
-
* escaping, ARG_MAX, and tmux paste-buffer flakiness for large prompts.
|
|
409
|
-
*
|
|
410
|
-
* The session dir and filename are slug-based (`prompt-<N>.txt` under
|
|
411
|
-
* `~/.atomic/sessions/...`), so they never contain shell-special characters.
|
|
412
|
-
*/
|
|
413
|
-
function readPromptInstruction(promptFile: string): string {
|
|
414
|
-
return `Read ${promptFile} and follow the instructions inside.`;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Spawn `claude` in the pane with the prompt baked in via the Read tool.
|
|
419
|
-
*
|
|
420
|
-
* The prompt is already written to `promptFile` by the caller. The spawn
|
|
421
|
-
* argv is `'Read the prompt in <path>'`, so Claude's first action is a Read
|
|
422
|
-
* tool call against that file.
|
|
423
|
-
*/
|
|
424
|
-
async function spawnClaudeWithPrompt(
|
|
425
|
-
paneId: string,
|
|
426
|
-
promptFile: string,
|
|
427
|
-
chatFlags: string[],
|
|
428
|
-
sessionId: string,
|
|
429
|
-
): Promise<void> {
|
|
430
|
-
const settingsPath = workflowHookSettingsPath();
|
|
431
|
-
const argvPrompt = `"${escBash(readPromptInstruction(promptFile))}"`;
|
|
432
|
-
const cmd = [
|
|
433
|
-
"claude",
|
|
434
|
-
...chatFlags,
|
|
435
|
-
// Workflow-owned hooks. Placed AFTER chatFlags so commander's last-wins
|
|
436
|
-
// semantics shadow any user-provided --settings, making this
|
|
437
|
-
// non-overridable by `.atomic/settings.json` chatFlags overrides. Passing
|
|
438
|
-
// a path avoids Claude Code's content-hashed /tmp/claude-settings*.json.
|
|
439
|
-
"--settings",
|
|
440
|
-
`"${escBash(settingsPath)}"`,
|
|
441
|
-
"--session-id",
|
|
442
|
-
sessionId,
|
|
443
|
-
argvPrompt,
|
|
444
|
-
].join(" ");
|
|
445
|
-
|
|
446
|
-
// Replace the pane's shell with `claude` directly. tmux execs the command
|
|
447
|
-
// itself, so there's no shell line editor to race with — the previous
|
|
448
|
-
// approach keystroked into a zsh that hadn't finished ZLE init yet, and
|
|
449
|
-
// zsh's TCSAFLUSH during startup would discard the buffered `\r`, leaving
|
|
450
|
-
// the command typed at the prompt but never submitted.
|
|
451
|
-
respawnPane(paneId, cmd);
|
|
452
|
-
|
|
453
|
-
// Positive readiness signal: wait for Claude's SessionStart hook (matcher
|
|
454
|
-
// `startup`) to write `~/.atomic/claude-ready/<session_id>`. This fires
|
|
455
|
-
// before Claude writes the JSONL transcript, so it beats the old
|
|
456
|
-
// transcript-file race and is deterministic.
|
|
457
|
-
await waitForReadyMarker(sessionId);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function workflowHookSettingsPath(): string {
|
|
461
|
-
const path = atomicContentTempPath(
|
|
462
|
-
"claude-settings-atomic",
|
|
463
|
-
".json",
|
|
464
|
-
WORKFLOW_HOOK_SETTINGS,
|
|
465
|
-
);
|
|
466
|
-
writeFileSync(path, WORKFLOW_HOOK_SETTINGS, {
|
|
467
|
-
encoding: "utf-8",
|
|
468
|
-
mode: 0o600,
|
|
469
|
-
});
|
|
470
|
-
return path;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Wait for the SessionStart hook's ready marker at
|
|
475
|
-
* `~/.atomic/claude-ready/<session_id>`.
|
|
476
|
-
*
|
|
477
|
-
* `atomic _claude-session-start-hook` is registered in
|
|
478
|
-
* {@link WORKFLOW_HOOK_SETTINGS} with matcher `startup`; the Claude CLI
|
|
479
|
-
* dispatches it during spawn, before the first API call and before the JSONL
|
|
480
|
-
* transcript is created. Waiting on the resulting marker file gives us a
|
|
481
|
-
* positive "Claude is alive" signal instead of racing the transcript writer.
|
|
482
|
-
*
|
|
483
|
-
* The timeout only fires on catastrophic startup failure (bad binary, exec
|
|
484
|
-
* error) — under load, Claude's own session bootstrap runs well under the
|
|
485
|
-
* limit because SessionStart is dispatched early in the startup sequence.
|
|
486
|
-
*/
|
|
487
|
-
async function waitForReadyMarker(sessionId: string): Promise<void> {
|
|
488
|
-
const { ready: readyDir } = claudeHookDirs();
|
|
489
|
-
await mkdir(readyDir, { recursive: true });
|
|
490
|
-
const target = join(readyDir, sessionId);
|
|
491
|
-
|
|
492
|
-
if (existsSync(target)) return;
|
|
493
|
-
|
|
494
|
-
const ac = new AbortController();
|
|
495
|
-
const timeout = setTimeout(() => ac.abort(), READY_HOOK_TIMEOUT_MS);
|
|
496
|
-
|
|
497
|
-
try {
|
|
498
|
-
await Promise.race([
|
|
499
|
-
// fs.watch — instant OS-native notification when the hook writes the file
|
|
500
|
-
(async (): Promise<void> => {
|
|
501
|
-
try {
|
|
502
|
-
for await (const _event of watch(readyDir, { signal: ac.signal })) {
|
|
503
|
-
// Trust disk state, not event.filename (Linux can deliver
|
|
504
|
-
// unexpected basenames under tmp+rename writes).
|
|
505
|
-
if (existsSync(target)) return;
|
|
506
|
-
}
|
|
507
|
-
} catch (e: unknown) {
|
|
508
|
-
if (e instanceof Error && e.name === "AbortError") throw e;
|
|
509
|
-
}
|
|
510
|
-
return new Promise<void>(() => {});
|
|
511
|
-
})(),
|
|
512
|
-
|
|
513
|
-
// Polling fallback — catches dropped inotify/FSEvent notifications
|
|
514
|
-
(async (): Promise<void> => {
|
|
515
|
-
while (!ac.signal.aborted) {
|
|
516
|
-
if (existsSync(target)) return;
|
|
517
|
-
await Bun.sleep(250);
|
|
518
|
-
}
|
|
519
|
-
throw new DOMException("Aborted", "AbortError");
|
|
520
|
-
})(),
|
|
521
|
-
]);
|
|
522
|
-
} catch (e: unknown) {
|
|
523
|
-
if (e instanceof DOMException && e.name === "AbortError") {
|
|
524
|
-
throw new Error(
|
|
525
|
-
`Timed out waiting for Claude SessionStart hook to signal readiness ` +
|
|
526
|
-
`at ${target}. Verify the \`claude\` command started successfully.`,
|
|
527
|
-
);
|
|
528
|
-
}
|
|
529
|
-
throw e;
|
|
530
|
-
} finally {
|
|
531
|
-
clearTimeout(timeout);
|
|
532
|
-
ac.abort();
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// ---------------------------------------------------------------------------
|
|
537
|
-
// HIL detection helpers
|
|
538
|
-
// ---------------------------------------------------------------------------
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* Returns true when the most recent assistant message in the transcript
|
|
542
|
-
* ended with `stop_reason: "tool_use"` — i.e. the agent stopped the current
|
|
543
|
-
* API response to call a tool but has not yet produced its post-tool answer.
|
|
544
|
-
*
|
|
545
|
-
* Claude Code's Stop hook fires each time Claude "finishes responding",
|
|
546
|
-
* which includes intermediate tool-use responses in a multi-step agent
|
|
547
|
-
* loop (not just the final `end_turn`). If we return from `waitForIdle`
|
|
548
|
-
* on the first Stop event, we capture the transcript mid-loop — the
|
|
549
|
-
* final assistant text block is still being generated and won't be on
|
|
550
|
-
* disk yet, so `inbox.md` drops the actual answer.
|
|
551
|
-
*
|
|
552
|
-
* We keep watching until we see an assistant message with a terminal
|
|
553
|
-
* stop_reason (`end_turn`, `max_tokens`, `stop_sequence`, `refusal`),
|
|
554
|
-
* which is the real end of the turn.
|
|
555
|
-
*
|
|
556
|
-
* Exported as `_isMidAgentLoop` for unit testing.
|
|
557
|
-
*/
|
|
558
|
-
export function _isMidAgentLoop(messages: SessionMessage[]): boolean {
|
|
559
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
560
|
-
const msg = messages[i];
|
|
561
|
-
if (msg?.type !== "assistant") continue;
|
|
562
|
-
const inner = msg.message as { stop_reason?: unknown } | null;
|
|
563
|
-
const stopReason = inner?.stop_reason;
|
|
564
|
-
return stopReason === "tool_use";
|
|
565
|
-
}
|
|
566
|
-
// No assistant message yet — treat as mid-loop so we wait for one.
|
|
567
|
-
return true;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* Watch `~/.atomic/claude-hil/` for this session's marker file and fire
|
|
572
|
-
* `onHIL(true|false)` on create/unlink. Returns when `signal` is aborted.
|
|
573
|
-
*
|
|
574
|
-
* The marker is written by the `_claude-ask-hook enter` subcommand from
|
|
575
|
-
* Claude Code's `PreToolUse` hook (matched on `AskUserQuestion`) and removed
|
|
576
|
-
* by `_claude-ask-hook exit` from `PostToolUse` / `PostToolUseFailure`. That
|
|
577
|
-
* makes the signal deterministic and independent of Claude Code's batched
|
|
578
|
-
* JSONL flush timing, which used to hide the HIL window entirely when
|
|
579
|
-
* tool_use and tool_result landed in the same file write.
|
|
580
|
-
*
|
|
581
|
-
* @internal Exported for tests.
|
|
582
|
-
*/
|
|
583
|
-
export async function watchHILMarker(
|
|
584
|
-
claudeSessionId: string,
|
|
585
|
-
onHIL: (waiting: boolean) => void,
|
|
586
|
-
signal: AbortSignal,
|
|
587
|
-
): Promise<void> {
|
|
588
|
-
const { hil: dir } = claudeHookDirs();
|
|
589
|
-
const target = join(dir, claudeSessionId);
|
|
590
|
-
|
|
591
|
-
await mkdir(dir, { recursive: true });
|
|
592
|
-
|
|
593
|
-
let wasHIL = false;
|
|
594
|
-
const emit = (isHIL: boolean): void => {
|
|
595
|
-
if (isHIL !== wasHIL) {
|
|
596
|
-
onHIL(isHIL);
|
|
597
|
-
wasHIL = isHIL;
|
|
598
|
-
}
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
// Attach the watcher BEFORE the initial existsSync so any event that fires
|
|
602
|
-
// during the check is buffered by the iterator instead of being dropped.
|
|
603
|
-
const watcher = watch(dir, { signal });
|
|
604
|
-
|
|
605
|
-
// Polling fallback: Bun/inotify can drop events under heavy fs load, which
|
|
606
|
-
// would leave the UI stuck on (or off) the blue "awaiting_input" pulse.
|
|
607
|
-
// A cheap periodic existsSync guarantees eventual consistency. `emit` is
|
|
608
|
-
// guarded by `wasHIL` so the interval is idempotent w.r.t. the watcher.
|
|
609
|
-
const poll = setInterval(() => emit(existsSync(target)), 250);
|
|
610
|
-
|
|
611
|
-
// Initial existsSync: handles resumed sessions whose PreToolUse marker was
|
|
612
|
-
// already on disk before the watcher attached.
|
|
613
|
-
if (existsSync(target)) emit(true);
|
|
614
|
-
|
|
615
|
-
try {
|
|
616
|
-
for await (const _event of watcher) {
|
|
617
|
-
// Don't trust event.filename — Bun/Linux deliver inconsistent basenames
|
|
618
|
-
// across OSes and write patterns. Disk existence is authoritative.
|
|
619
|
-
emit(existsSync(target));
|
|
620
|
-
}
|
|
621
|
-
} catch (e: unknown) {
|
|
622
|
-
if (!(e instanceof Error && e.name === "AbortError")) {
|
|
623
|
-
throw e;
|
|
624
|
-
}
|
|
625
|
-
} finally {
|
|
626
|
-
clearInterval(poll);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// ---------------------------------------------------------------------------
|
|
631
|
-
// Helpers
|
|
632
|
-
// ---------------------------------------------------------------------------
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* Path of the directory where the claude-stop-hook writes marker files.
|
|
636
|
-
* Each Claude turn creates `~/.atomic/claude-stop/<session_id>` which
|
|
637
|
-
* triggers the `fs.watch` event in `waitForIdle`.
|
|
638
|
-
*
|
|
639
|
-
* @internal Exported for unit tests.
|
|
640
|
-
*/
|
|
641
|
-
export function markerDir(): string {
|
|
642
|
-
return claudeHookDirs().marker;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* Return the marker file path for a given Claude session ID.
|
|
647
|
-
*
|
|
648
|
-
* @internal Exported for unit tests.
|
|
649
|
-
*/
|
|
650
|
-
export function markerPath(claudeSessionId: string): string {
|
|
651
|
-
return join(markerDir(), claudeSessionId);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Directory where the workflow runtime writes queued follow-up prompts that
|
|
656
|
-
* `atomic _claude-stop-hook` picks up and feeds back to Claude as
|
|
657
|
-
* `{decision:"block", reason:<prompt>}`. @internal Exported for unit tests.
|
|
658
|
-
*/
|
|
659
|
-
export function queueDir(): string {
|
|
660
|
-
return claudeHookDirs().queue;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
/** Return the queue file path for a given Claude session ID. @internal */
|
|
664
|
-
export function queuePath(claudeSessionId: string): string {
|
|
665
|
-
return join(queueDir(), claudeSessionId);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* Directory where the runtime writes session-release signals. When the Stop
|
|
670
|
-
* hook sees `~/.atomic/claude-release/<session_id>` it exits 0 without
|
|
671
|
-
* emitting a block decision — the signal used by `clearClaudeSession` to
|
|
672
|
-
* tell Claude it's safe to actually stop. @internal Exported for unit tests.
|
|
673
|
-
*/
|
|
674
|
-
export function releaseDir(): string {
|
|
675
|
-
return claudeHookDirs().release;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
/** Return the release file path for a given Claude session ID. @internal */
|
|
679
|
-
export function releasePath(claudeSessionId: string): string {
|
|
680
|
-
return join(releaseDir(), claudeSessionId);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* Ensure the marker directory exists and remove any stale marker left from a
|
|
685
|
-
* previous turn of this session. Call this BEFORE submitting the prompt so
|
|
686
|
-
* the subsequent `waitForIdle` watch loop doesn't fire on a stale file.
|
|
687
|
-
*
|
|
688
|
-
* Ignores ENOENT on `unlink` — the file simply doesn't exist yet.
|
|
689
|
-
*/
|
|
690
|
-
async function clearStaleMarker(claudeSessionId: string): Promise<void> {
|
|
691
|
-
await mkdir(markerDir(), { recursive: true });
|
|
692
|
-
try {
|
|
693
|
-
await unlink(markerPath(claudeSessionId));
|
|
694
|
-
} catch (e: unknown) {
|
|
695
|
-
// ENOENT is expected — ignore it; rethrow anything else
|
|
696
|
-
if (!(e instanceof Error && "code" in e && (e as NodeJS.ErrnoException).code === "ENOENT")) {
|
|
697
|
-
throw e;
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
* Ensure the queue directory exists and remove any stale entry from a prior
|
|
704
|
-
* turn so the Stop hook doesn't race on it. Ignores ENOENT.
|
|
705
|
-
*/
|
|
706
|
-
async function clearStaleQueue(claudeSessionId: string): Promise<void> {
|
|
707
|
-
await mkdir(queueDir(), { recursive: true });
|
|
708
|
-
try {
|
|
709
|
-
await unlink(queuePath(claudeSessionId));
|
|
710
|
-
} catch (e: unknown) {
|
|
711
|
-
if (!(e instanceof Error && "code" in e && (e as NodeJS.ErrnoException).code === "ENOENT")) {
|
|
712
|
-
throw e;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
/**
|
|
718
|
-
* Remove a stale HIL marker left over from a prior turn (e.g. the ask-hook
|
|
719
|
-
* process was SIGKILL'd between PreToolUse and PostToolUse). Without this,
|
|
720
|
-
* `watchHILMarker`'s initial `existsSync` would spuriously fire `onHIL(true)`
|
|
721
|
-
* at the start of a fresh turn. Ignores ENOENT.
|
|
722
|
-
*/
|
|
723
|
-
async function clearStaleHILMarker(claudeSessionId: string): Promise<void> {
|
|
724
|
-
const { hil } = claudeHookDirs();
|
|
725
|
-
await mkdir(hil, { recursive: true });
|
|
726
|
-
try {
|
|
727
|
-
await unlink(join(hil, claudeSessionId));
|
|
728
|
-
} catch (e: unknown) {
|
|
729
|
-
if (!(e instanceof Error && "code" in e && (e as NodeJS.ErrnoException).code === "ENOENT")) {
|
|
730
|
-
throw e;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* Remove a stale ready marker from a prior session that reused this UUID (in
|
|
737
|
-
* practice impossible — UUIDs are fresh per session — but cheap insurance so
|
|
738
|
-
* `waitForReadyMarker`'s initial existsSync can't false-positive on anything
|
|
739
|
-
* we left behind). Ignores ENOENT.
|
|
740
|
-
*/
|
|
741
|
-
async function clearStaleReadyMarker(claudeSessionId: string): Promise<void> {
|
|
742
|
-
const { ready } = claudeHookDirs();
|
|
743
|
-
await mkdir(ready, { recursive: true });
|
|
744
|
-
try {
|
|
745
|
-
await unlink(join(ready, claudeSessionId));
|
|
746
|
-
} catch (e: unknown) {
|
|
747
|
-
if (!(e instanceof Error && "code" in e && (e as NodeJS.ErrnoException).code === "ENOENT")) {
|
|
748
|
-
throw e;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/**
|
|
754
|
-
* Write the next prompt to the session queue file. The currently-running
|
|
755
|
-
* Stop hook process (blocked on poll from the previous turn) picks it up,
|
|
756
|
-
* emits `{decision:"block", reason:<prompt>}` on stdout, and Claude feeds
|
|
757
|
-
* it back as the next user message — no tmux keystrokes required.
|
|
758
|
-
*/
|
|
759
|
-
async function enqueuePrompt(claudeSessionId: string, prompt: string): Promise<void> {
|
|
760
|
-
await mkdir(queueDir(), { recursive: true });
|
|
761
|
-
await writeFile(queuePath(claudeSessionId), prompt, "utf-8");
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
/**
|
|
765
|
-
* Signal the Stop hook's blocking wait that this session is done. Called
|
|
766
|
-
* during session teardown so the final hook invocation exits 0 promptly.
|
|
767
|
-
* Safe to call more than once.
|
|
768
|
-
*/
|
|
769
|
-
export async function releaseClaudeSession(claudeSessionId: string): Promise<void> {
|
|
770
|
-
await mkdir(releaseDir(), { recursive: true });
|
|
771
|
-
await writeFile(releasePath(claudeSessionId), "");
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/** @internal */
|
|
775
|
-
function pidDir(): string {
|
|
776
|
-
return claudeHookDirs().pid;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
/** @internal */
|
|
780
|
-
function pidFilePath(claudeSessionId: string): string {
|
|
781
|
-
return join(pidDir(), claudeSessionId);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
/**
|
|
785
|
-
* Write `process.pid` to `~/.atomic/claude-pid/<session_id>` so the Stop hook
|
|
786
|
-
* can use it as a liveness signal. If atomic is SIGKILL'd (no chance to run
|
|
787
|
-
* `clearClaudeSession`), the hook detects the dead PID via `process.kill(..,0)`
|
|
788
|
-
* and self-exits instead of parking Claude for the full 24-day timeout.
|
|
789
|
-
*/
|
|
790
|
-
async function writeAtomicPidFile(claudeSessionId: string): Promise<void> {
|
|
791
|
-
await mkdir(pidDir(), { recursive: true });
|
|
792
|
-
await writeFile(pidFilePath(claudeSessionId), String(process.pid), "utf-8");
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
/** Remove the pid file for a session. Idempotent — ENOENT is swallowed. */
|
|
796
|
-
async function unlinkAtomicPidFile(claudeSessionId: string): Promise<void> {
|
|
797
|
-
try {
|
|
798
|
-
await unlink(pidFilePath(claudeSessionId));
|
|
799
|
-
} catch (e: unknown) {
|
|
800
|
-
if (!(e instanceof Error && "code" in e && (e as NodeJS.ErrnoException).code === "ENOENT")) {
|
|
801
|
-
throw e;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
// ---------------------------------------------------------------------------
|
|
807
|
-
// Idle detection via marker file watch
|
|
808
|
-
// ---------------------------------------------------------------------------
|
|
809
|
-
|
|
810
|
-
/**
|
|
811
|
-
* Wait for the Claude session to become idle using `fs.watch` on the
|
|
812
|
-
* `~/.atomic/claude-stop/` marker directory.
|
|
813
|
-
*
|
|
814
|
-
* When Claude finishes a turn, the `atomic _claude-stop-hook` Stop hook writes
|
|
815
|
-
* `~/.atomic/claude-stop/<session_id>`. The write triggers an OS-native
|
|
816
|
-
* `fs.watch` event on the parent directory — far more reliable than polling
|
|
817
|
-
* tmux pane glyphs, which vary between Claude Code versions.
|
|
818
|
-
*
|
|
819
|
-
* This function is strictly about *idle detection*. HIL is detected separately
|
|
820
|
-
* by {@link watchHILMarker}; the Stop hook does not fire while
|
|
821
|
-
* `AskUserQuestion` is pending (the agent loop blocks on deferred tools), so
|
|
822
|
-
* mixing the two would silently miss the HIL window.
|
|
823
|
-
*
|
|
824
|
-
* Algorithm:
|
|
825
|
-
* 1. Attach the directory watcher, then check for the marker file on disk —
|
|
826
|
-
* this closes the race where the Stop hook fires between prompt submission
|
|
827
|
-
* and watcher attach.
|
|
828
|
-
* 2. On any event, re-check the marker file on disk (we intentionally do NOT
|
|
829
|
-
* filter by `event.filename`, because on Linux a write can deliver multiple
|
|
830
|
-
* events with varying filenames and editor tools may race us).
|
|
831
|
-
* 3. Read the session transcript via `getSessionMessages` and slice messages
|
|
832
|
-
* from `transcriptBeforeCount`.
|
|
833
|
-
* 4. Clean up the `fs.watch` watcher on any exit path via AbortController.
|
|
834
|
-
*
|
|
835
|
-
* @param claudeSessionId - Claude's session UUID (used to identify marker file)
|
|
836
|
-
* @param transcriptBeforeCount - number of messages in transcript before this turn
|
|
837
|
-
*/
|
|
838
|
-
/**
|
|
839
|
-
* @internal Exported for unit tests.
|
|
840
|
-
*/
|
|
841
|
-
export async function waitForIdle(
|
|
842
|
-
claudeSessionId: string,
|
|
843
|
-
transcriptBeforeCount: number,
|
|
844
|
-
): Promise<SessionMessage[]> {
|
|
845
|
-
|
|
846
|
-
const dir = markerDir();
|
|
847
|
-
const sessionId = claudeSessionId;
|
|
848
|
-
const target = markerPath(sessionId);
|
|
849
|
-
const ac = new AbortController();
|
|
850
|
-
|
|
851
|
-
// Process a marker that has appeared on disk. Returns a tuple:
|
|
852
|
-
// [resolved, result] — when resolved=true, waitForIdle should return.
|
|
853
|
-
const readMessages = async (): Promise<SessionMessage[] | null> => {
|
|
854
|
-
try {
|
|
855
|
-
return await getSessionMessages(sessionId, {
|
|
856
|
-
dir: process.cwd(),
|
|
857
|
-
includeSystemMessages: true,
|
|
858
|
-
});
|
|
859
|
-
} catch {
|
|
860
|
-
return null;
|
|
861
|
-
}
|
|
862
|
-
};
|
|
863
|
-
|
|
864
|
-
const handleMarker = async (): Promise<[boolean, SessionMessage[]]> => {
|
|
865
|
-
let msgs = await readMessages();
|
|
866
|
-
if (msgs === null) {
|
|
867
|
-
// Transcript read failed — keep watching; the next event will retry.
|
|
868
|
-
return [false, []];
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
// The Stop hook fires only once per agent loop completion (when there
|
|
872
|
-
// are no more tool_use blocks to resolve — see Claude Code's
|
|
873
|
-
// `src/query/stopHooks.ts` / `query.ts`: `if (!needsFollowUp)`). But
|
|
874
|
-
// Claude Code writes to the JSONL transcript asynchronously via
|
|
875
|
-
// `enqueueWrite()` with a batched ~100ms flush, so the final
|
|
876
|
-
// `assistant[text]` message can still be in the page-cache when our
|
|
877
|
-
// marker watcher fires. Reading the transcript at that moment races
|
|
878
|
-
// the writer and returns a prefix ending at `user[tool_result]`.
|
|
879
|
-
//
|
|
880
|
-
// Because no further marker events are coming, we can't just "keep
|
|
881
|
-
// watching the marker dir". Instead, poll the transcript file directly
|
|
882
|
-
// until it either settles on a terminal stop_reason or the poll budget
|
|
883
|
-
// expires. The budget covers Claude Code's flush interval plus headroom
|
|
884
|
-
// for slow disks and buffered `fs/promises` writes.
|
|
885
|
-
if (_isMidAgentLoop(msgs)) {
|
|
886
|
-
const pollIntervalMs = 50;
|
|
887
|
-
const pollBudgetMs = 3_000;
|
|
888
|
-
const start = Date.now();
|
|
889
|
-
while (_isMidAgentLoop(msgs) && Date.now() - start < pollBudgetMs) {
|
|
890
|
-
await Bun.sleep(pollIntervalMs);
|
|
891
|
-
const next = await readMessages();
|
|
892
|
-
if (next) msgs = next;
|
|
893
|
-
}
|
|
894
|
-
// Whether we recovered or ran out of budget, fall through — returning
|
|
895
|
-
// what we have beats hanging forever if the writer really did drop a
|
|
896
|
-
// message (e.g. max-tokens collapse, abort mid-stream).
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
const sliced = msgs.length > transcriptBeforeCount
|
|
900
|
-
? msgs.slice(transcriptBeforeCount)
|
|
901
|
-
: [];
|
|
902
|
-
return [true, sliced];
|
|
903
|
-
};
|
|
904
|
-
|
|
905
|
-
try {
|
|
906
|
-
// Attach the watcher FIRST; fs.watch returns an iterable whose underlying
|
|
907
|
-
// inotify/FSEvent subscription is live from this point on.
|
|
908
|
-
const watcher = watch(dir, { signal: ac.signal });
|
|
909
|
-
|
|
910
|
-
// Close the race: if the Stop hook fired between clearStaleMarker() and
|
|
911
|
-
// the watcher attach above, the marker is already on disk and no further
|
|
912
|
-
// events will be emitted. Handle it synchronously.
|
|
913
|
-
if (existsSync(target)) {
|
|
914
|
-
const [done, result] = await handleMarker();
|
|
915
|
-
if (done) {
|
|
916
|
-
ac.abort();
|
|
917
|
-
return result;
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
for await (const _event of watcher) {
|
|
922
|
-
// We don't trust event.filename — on Linux, a tmp+rename write emits
|
|
923
|
-
// events with the `.tmp` basename, and other files in the marker dir
|
|
924
|
-
// can race us. The marker file's existence on disk is authoritative.
|
|
925
|
-
if (!existsSync(target)) continue;
|
|
926
|
-
|
|
927
|
-
const [done, result] = await handleMarker();
|
|
928
|
-
if (done) {
|
|
929
|
-
ac.abort();
|
|
930
|
-
return result;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
} catch (e: unknown) {
|
|
934
|
-
// AbortError is expected when we call ac.abort() to stop watching.
|
|
935
|
-
if (!(e instanceof Error && e.name === "AbortError")) {
|
|
936
|
-
throw e;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
return [];
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// ---------------------------------------------------------------------------
|
|
944
|
-
// claudeQuery
|
|
945
|
-
// ---------------------------------------------------------------------------
|
|
946
|
-
|
|
947
|
-
export interface ClaudeQueryOptions {
|
|
948
|
-
/** tmux pane ID where Claude is running */
|
|
949
|
-
paneId: string;
|
|
950
|
-
/** The prompt to send */
|
|
951
|
-
prompt: string;
|
|
952
|
-
/**
|
|
953
|
-
* Called when the agent's human-in-the-loop state changes.
|
|
954
|
-
* `waiting=true` → AskUserQuestion is pending (agent blocked on user input).
|
|
955
|
-
* `waiting=false` → AskUserQuestion was resolved (agent resumed processing).
|
|
956
|
-
*/
|
|
957
|
-
onHIL?: (waiting: boolean) => void;
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
/**
|
|
961
|
-
* Extract text content from assistant messages in a transcript slice.
|
|
962
|
-
*
|
|
963
|
-
* Walks messages from `afterIndex` forward, pulls `TextBlock.text` from each
|
|
964
|
-
* assistant message's content array, and joins them. The `message` payload is
|
|
965
|
-
* `unknown` in the SDK type so we do runtime narrowing.
|
|
966
|
-
*
|
|
967
|
-
* Exported so workflow authors can extract text from `SessionMessage[]`
|
|
968
|
-
* returned by `s.session.query()`.
|
|
969
|
-
*/
|
|
970
|
-
export function extractAssistantText(
|
|
971
|
-
msgs: ReadonlyArray<{ type: string; message: unknown }>,
|
|
972
|
-
afterIndex: number,
|
|
973
|
-
): string {
|
|
974
|
-
const parts: string[] = [];
|
|
975
|
-
for (let i = afterIndex; i < msgs.length; i++) {
|
|
976
|
-
const msg = msgs[i];
|
|
977
|
-
if (!msg || msg.type !== "assistant") continue;
|
|
978
|
-
const m = msg.message;
|
|
979
|
-
if (!m || typeof m !== "object") continue;
|
|
980
|
-
const content = (m as Record<string, unknown>).content;
|
|
981
|
-
if (!Array.isArray(content)) continue;
|
|
982
|
-
for (const block of content) {
|
|
983
|
-
if (
|
|
984
|
-
block &&
|
|
985
|
-
typeof block === "object" &&
|
|
986
|
-
(block as Record<string, unknown>).type === "text"
|
|
987
|
-
) {
|
|
988
|
-
parts.push(String((block as Record<string, unknown>).text ?? ""));
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
return parts.join("\n");
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
/**
|
|
996
|
-
* Send a prompt to a Claude Code interactive session running in a tmux pane.
|
|
997
|
-
*
|
|
998
|
-
* First query and follow-up queries use different delivery channels:
|
|
999
|
-
*
|
|
1000
|
-
* - **First query**: stages the prompt in a tmp file and spawns
|
|
1001
|
-
* `claude --session-id <UUID> 'Read the prompt in <path>'` into the
|
|
1002
|
-
* empty pane. Claude's first action is a Read tool call, which
|
|
1003
|
-
* sidesteps ARG_MAX on the spawn argv.
|
|
1004
|
-
*
|
|
1005
|
-
* - **Follow-up query**: writes the prompt to
|
|
1006
|
-
* `~/.atomic/claude-queue/<session_id>`. The Stop hook from the
|
|
1007
|
-
* previous turn is blocked in a poll loop there; it reads the queue
|
|
1008
|
-
* entry and emits `{"decision":"block","reason":<prompt>}` on stdout,
|
|
1009
|
-
* which Claude Code feeds back as the next user message. No tmux
|
|
1010
|
-
* keystrokes, no paste-buffer dance, no pane-state polling — the
|
|
1011
|
-
* whole delivery rides Claude's own continuation API.
|
|
1012
|
-
*
|
|
1013
|
-
* Both paths converge on `waitForIdle`, which watches the Stop-hook marker
|
|
1014
|
-
* file for this session and returns the transcript slice for the turn.
|
|
1015
|
-
*
|
|
1016
|
-
* @example
|
|
1017
|
-
* ```typescript
|
|
1018
|
-
* import { claudeQuery } from "@bastani/atomic/workflows";
|
|
1019
|
-
*
|
|
1020
|
-
* const result = await claudeQuery({
|
|
1021
|
-
* paneId: ctx.paneId,
|
|
1022
|
-
* prompt: "Describe this project",
|
|
1023
|
-
* });
|
|
1024
|
-
* ctx.log(result.output);
|
|
1025
|
-
* ```
|
|
1026
|
-
*/
|
|
1027
|
-
export async function claudeQuery(options: ClaudeQueryOptions): Promise<SessionMessage[]> {
|
|
1028
|
-
const { paneId, prompt, onHIL } = options;
|
|
1029
|
-
|
|
1030
|
-
const paneState = initializedPanes.get(paneId);
|
|
1031
|
-
if (!paneState) {
|
|
1032
|
-
throw new Error(
|
|
1033
|
-
"claudeQuery() called without a prior createClaudeSession() for this pane. " +
|
|
1034
|
-
"Call createClaudeSession({ paneId }) first to start the Claude CLI.",
|
|
1035
|
-
);
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
const dir = process.cwd();
|
|
1039
|
-
const claudeSessionId = paneState.claudeSessionId;
|
|
1040
|
-
|
|
1041
|
-
// Clear stale marker AND stale queue entry before submitting so the
|
|
1042
|
-
// Stop-hook for the previous turn (if any) cannot race this one. The HIL
|
|
1043
|
-
// marker is cleared too so a crashed ask-hook process from turn N-1 can't
|
|
1044
|
-
// make `watchHILMarker`'s initial existsSync spuriously fire onHIL(true).
|
|
1045
|
-
await clearStaleMarker(claudeSessionId);
|
|
1046
|
-
await clearStaleQueue(claudeSessionId);
|
|
1047
|
-
await clearStaleHILMarker(claudeSessionId);
|
|
1048
|
-
await clearStaleReadyMarker(claudeSessionId);
|
|
1049
|
-
|
|
1050
|
-
let transcriptBeforeCount = 0;
|
|
1051
|
-
let spawnPromptFile: string | undefined;
|
|
1052
|
-
|
|
1053
|
-
try {
|
|
1054
|
-
if (paneState.claudeStarted) {
|
|
1055
|
-
// Follow-up query: snapshot the transcript length so waitForIdle can
|
|
1056
|
-
// slice out the messages produced by THIS turn, then enqueue the
|
|
1057
|
-
// prompt for the Stop hook to pick up.
|
|
1058
|
-
try {
|
|
1059
|
-
const msgs = await getSessionMessages(claudeSessionId, {
|
|
1060
|
-
dir,
|
|
1061
|
-
includeSystemMessages: true,
|
|
1062
|
-
});
|
|
1063
|
-
transcriptBeforeCount = msgs.length;
|
|
1064
|
-
} catch {
|
|
1065
|
-
// Best-effort — 0 means we scan all messages (correct, slightly less efficient)
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
await enqueuePrompt(claudeSessionId, prompt);
|
|
1069
|
-
} else {
|
|
1070
|
-
// First query: spawn claude with the prompt baked into argv via the
|
|
1071
|
-
// Read-tool indirection. The tmp file only has to live long enough
|
|
1072
|
-
// for Claude's first Read tool call, so we delete it once waitForIdle
|
|
1073
|
-
// returns (the turn is complete by then).
|
|
1074
|
-
spawnPromptFile = atomicTempPath(
|
|
1075
|
-
"atomic-claude-prompt",
|
|
1076
|
-
".txt",
|
|
1077
|
-
`${claudeSessionId}-${randomUUID()}`,
|
|
1078
|
-
);
|
|
1079
|
-
writeFileSync(spawnPromptFile, prompt, {
|
|
1080
|
-
encoding: "utf-8",
|
|
1081
|
-
mode: 0o600,
|
|
1082
|
-
});
|
|
1083
|
-
|
|
1084
|
-
await spawnClaudeWithPrompt(
|
|
1085
|
-
paneId,
|
|
1086
|
-
spawnPromptFile,
|
|
1087
|
-
paneState.chatFlags,
|
|
1088
|
-
claudeSessionId,
|
|
1089
|
-
);
|
|
1090
|
-
paneState.claudeStarted = true;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
// HIL detection runs in parallel with idle detection. The
|
|
1094
|
-
// PreToolUse/PostToolUse/PostToolUseFailure hooks on `AskUserQuestion`
|
|
1095
|
-
// write/remove `~/.atomic/claude-hil/<session_id>`; we watch that dir
|
|
1096
|
-
// for create/unlink events so HIL state is deterministic and immune to
|
|
1097
|
-
// Claude Code's batched JSONL flush timing.
|
|
1098
|
-
const hilAc = new AbortController();
|
|
1099
|
-
if (onHIL) {
|
|
1100
|
-
void watchHILMarker(claudeSessionId, onHIL, hilAc.signal).catch(() => {
|
|
1101
|
-
// Best-effort — never fail the query over HIL detection.
|
|
1102
|
-
});
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
try {
|
|
1106
|
-
return await waitForIdle(claudeSessionId, transcriptBeforeCount);
|
|
1107
|
-
} finally {
|
|
1108
|
-
hilAc.abort();
|
|
1109
|
-
// Safety: waitForIdle only returns at true turn-idle. If the ask-hook
|
|
1110
|
-
// process crashed mid-turn and left the marker on disk, the UI could
|
|
1111
|
-
// be stuck on awaiting_input. `resumeSession` in the panel store is
|
|
1112
|
-
// idempotent (no-op when the session isn't in awaiting_input), so
|
|
1113
|
-
// this is always safe.
|
|
1114
|
-
onHIL?.(false);
|
|
1115
|
-
}
|
|
1116
|
-
} finally {
|
|
1117
|
-
if (spawnPromptFile) {
|
|
1118
|
-
try {
|
|
1119
|
-
await unlink(spawnPromptFile);
|
|
1120
|
-
} catch {
|
|
1121
|
-
// ENOENT / already removed is fine.
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
// ---------------------------------------------------------------------------
|
|
1128
|
-
// Synthetic wrappers — uniform s.client / s.session API for Claude stages
|
|
1129
|
-
// ---------------------------------------------------------------------------
|
|
1130
|
-
|
|
1131
|
-
/**
|
|
1132
|
-
* Merge two `disallowedTools` lists, preserving caller entries and appending
|
|
1133
|
-
* any extras that aren't already present. Exported for unit testing.
|
|
1134
|
-
*/
|
|
1135
|
-
export function mergeDisallowedTools(
|
|
1136
|
-
existing: string[] | undefined,
|
|
1137
|
-
extras: string[],
|
|
1138
|
-
): string[] {
|
|
1139
|
-
const merged = [...(existing ?? [])];
|
|
1140
|
-
for (const tool of extras) {
|
|
1141
|
-
if (!merged.includes(tool)) merged.push(tool);
|
|
1142
|
-
}
|
|
1143
|
-
return merged;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
/**
|
|
1147
|
-
* Fold the atomic-managed additional instructions into a caller's
|
|
1148
|
-
* `systemPrompt` value. Behavior, in order of precedence:
|
|
1149
|
-
*
|
|
1150
|
-
* - **No caller value** → return a `claude_code` preset with our content
|
|
1151
|
-
* in `append`. Preserves the SDK's full Claude Code persona.
|
|
1152
|
-
* - **Caller passed a preset object** → concatenate our content onto the
|
|
1153
|
-
* existing `append` (newline-separated when both are present).
|
|
1154
|
-
* - **Caller passed a custom string or array** → leave it alone. The
|
|
1155
|
-
* caller has explicitly opted into a custom prompt, and silently
|
|
1156
|
-
* prepending the persona-style preset text would break that contract.
|
|
1157
|
-
*
|
|
1158
|
-
* Exported for unit testing.
|
|
1159
|
-
*/
|
|
1160
|
-
export function mergeSystemPromptAppend(
|
|
1161
|
-
existing: SDKOptions["systemPrompt"],
|
|
1162
|
-
extra: string,
|
|
1163
|
-
): SDKOptions["systemPrompt"] {
|
|
1164
|
-
if (!extra) return existing;
|
|
1165
|
-
if (existing === undefined) {
|
|
1166
|
-
return { type: "preset", preset: "claude_code", append: extra };
|
|
1167
|
-
}
|
|
1168
|
-
if (typeof existing === "object" && !Array.isArray(existing) && existing.type === "preset") {
|
|
1169
|
-
const prevAppend = existing.append ?? "";
|
|
1170
|
-
const merged = prevAppend ? `${prevAppend}\n\n${extra}` : extra;
|
|
1171
|
-
return { ...existing, append: merged };
|
|
1172
|
-
}
|
|
1173
|
-
return existing;
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
/**
|
|
1177
|
-
* Synthetic client wrapper for Claude stages.
|
|
1178
|
-
* Auto-starts the Claude CLI in the tmux pane during `start()`.
|
|
1179
|
-
*/
|
|
1180
|
-
export class ClaudeClientWrapper {
|
|
1181
|
-
readonly paneId: string;
|
|
1182
|
-
private readonly opts: { chatFlags?: string[] };
|
|
1183
|
-
|
|
1184
|
-
constructor(
|
|
1185
|
-
paneId: string,
|
|
1186
|
-
opts: { chatFlags?: string[] } = {},
|
|
1187
|
-
) {
|
|
1188
|
-
this.paneId = paneId;
|
|
1189
|
-
this.opts = opts;
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* Start the Claude CLI in the tmux pane. Returns the Claude session UUID
|
|
1194
|
-
* so the caller can pass it to `ClaudeSessionWrapper` (and thus expose it
|
|
1195
|
-
* as `s.sessionId` to workflows). This is the UUID used by Claude Code to
|
|
1196
|
-
* name its JSONL transcript file and to key the Stop-hook marker — workflows
|
|
1197
|
-
* pass it to `s.save(s.sessionId)` so the save path reads the correct
|
|
1198
|
-
* transcript even when many Claude sessions run in parallel.
|
|
1199
|
-
*/
|
|
1200
|
-
async start(): Promise<string> {
|
|
1201
|
-
return await createClaudeSession({
|
|
1202
|
-
paneId: this.paneId,
|
|
1203
|
-
chatFlags: this.opts.chatFlags,
|
|
1204
|
-
});
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
/** Noop — cleanup is handled by the runtime via `clearClaudeSession`. */
|
|
1208
|
-
async stop(): Promise<void> {}
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
/**
|
|
1212
|
-
* Synthetic session wrapper for Claude stages.
|
|
1213
|
-
* Wraps `claudeQuery()` so users call `s.session.query(prompt)`.
|
|
1214
|
-
*/
|
|
1215
|
-
export class ClaudeSessionWrapper {
|
|
1216
|
-
readonly paneId: string;
|
|
1217
|
-
readonly sessionId: string;
|
|
1218
|
-
private readonly onHIL: ((waiting: boolean) => void) | undefined;
|
|
1219
|
-
|
|
1220
|
-
constructor(
|
|
1221
|
-
paneId: string,
|
|
1222
|
-
sessionId: string,
|
|
1223
|
-
onHIL?: (waiting: boolean) => void,
|
|
1224
|
-
) {
|
|
1225
|
-
this.paneId = paneId;
|
|
1226
|
-
this.sessionId = sessionId;
|
|
1227
|
-
this.onHIL = onHIL;
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
/**
|
|
1231
|
-
* Send a prompt to Claude and wait for the response.
|
|
1232
|
-
*
|
|
1233
|
-
* The `_options` parameter exists for signature compatibility with
|
|
1234
|
-
* {@link HeadlessClaudeSessionWrapper#query} (which forwards SDK options
|
|
1235
|
-
* like `agent`, `permissionMode`, etc. to the Agent SDK). In the
|
|
1236
|
-
* interactive pane path these options don't apply — we're driving the
|
|
1237
|
-
* `claude` CLI binary, not the SDK — so they are silently ignored.
|
|
1238
|
-
*/
|
|
1239
|
-
async query(
|
|
1240
|
-
prompt: string,
|
|
1241
|
-
_options?: Partial<SDKOptions>,
|
|
1242
|
-
): Promise<SessionMessage[]> {
|
|
1243
|
-
return claudeQuery({
|
|
1244
|
-
paneId: this.paneId,
|
|
1245
|
-
prompt,
|
|
1246
|
-
onHIL: this.onHIL,
|
|
1247
|
-
});
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
/**
|
|
1251
|
-
* Structured output is only produced by the Agent SDK's `result` message,
|
|
1252
|
-
* which interactive stages don't consume (they drive the `claude` CLI via
|
|
1253
|
-
* tmux, not the SDK). Always `undefined` here — pair `outputFormat` with a
|
|
1254
|
-
* headless stage to read {@link HeadlessClaudeSessionWrapper#lastStructuredOutput}.
|
|
1255
|
-
*/
|
|
1256
|
-
get lastStructuredOutput(): unknown {
|
|
1257
|
-
return undefined;
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
/** Noop — for API symmetry with CopilotSession.disconnect(). */
|
|
1261
|
-
async disconnect(): Promise<void> {}
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
// ---------------------------------------------------------------------------
|
|
1265
|
-
// Headless wrappers — use the Agent SDK directly (no tmux pane)
|
|
1266
|
-
// ---------------------------------------------------------------------------
|
|
1267
|
-
|
|
1268
|
-
/**
|
|
1269
|
-
* Headless client wrapper for Claude stages. No tmux pane — noop start/stop.
|
|
1270
|
-
* Used when `options.headless` is true in `ctx.stage()`.
|
|
1271
|
-
*/
|
|
1272
|
-
export class HeadlessClaudeClientWrapper {
|
|
1273
|
-
/**
|
|
1274
|
-
* Headless Claude stages don't pre-allocate a session — each `query()` call
|
|
1275
|
-
* to {@link HeadlessClaudeSessionWrapper} spawns a fresh Agent SDK run that
|
|
1276
|
-
* emits its own `session_id`. We still return an empty string here so the
|
|
1277
|
-
* method signature matches {@link ClaudeClientWrapper#start}.
|
|
1278
|
-
*/
|
|
1279
|
-
async start(): Promise<string> {
|
|
1280
|
-
return "";
|
|
1281
|
-
}
|
|
1282
|
-
async stop(): Promise<void> {}
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
/**
|
|
1286
|
-
* Resolve the `claude` CLI binary for headless SDK queries.
|
|
1287
|
-
*
|
|
1288
|
-
* Pins the SDK to the same binary interactive stages already spawn via tmux
|
|
1289
|
-
* (`AGENT_CONFIG.claude.cmd` on PATH), bypassing
|
|
1290
|
-
* `@anthropic-ai/claude-agent-sdk`'s built-in resolver. That resolver probes
|
|
1291
|
-
* optional native packages in a fixed order — on Linux it tries
|
|
1292
|
-
* `linux-${arch}-musl` before `linux-${arch}` and returns whichever
|
|
1293
|
-
* `require.resolve` finds first — so on a glibc host where both optional
|
|
1294
|
-
* packages got installed (Bun installs every optionalDependency by default)
|
|
1295
|
-
* it picks the musl binary, which can't exec because its dynamic linker
|
|
1296
|
-
* (`/lib/ld-musl-*.so.1`) is absent. The SDK surfaces the resulting ENOENT
|
|
1297
|
-
* as a misleading "Claude Code native binary not found" error.
|
|
1298
|
-
*
|
|
1299
|
-
* `chatCommand` and `workflowCommand` already fail fast when `claude` isn't
|
|
1300
|
-
* on PATH (see `isCommandInstalled` in each), so in practice this lookup
|
|
1301
|
-
* always succeeds. The throw here is a belt-and-suspenders guard that
|
|
1302
|
-
* prefers a clear failure over silently falling back to the SDK's resolver.
|
|
1303
|
-
*/
|
|
1304
|
-
export function resolveHeadlessClaudeBin(): string {
|
|
1305
|
-
// Pass PATH explicitly — the 1-arg form of Bun.which caches the value
|
|
1306
|
-
// captured at process start, which makes the lookup insensitive to later
|
|
1307
|
-
// env mutations (and un-exercisable from tests that tweak `process.env.PATH`).
|
|
1308
|
-
const onPath = Bun.which("claude", { PATH: process.env.PATH ?? "" });
|
|
1309
|
-
if (!onPath) {
|
|
1310
|
-
throw new Error(
|
|
1311
|
-
"`claude` CLI not found on PATH. Install Claude Code via the native " +
|
|
1312
|
-
"installer (https://docs.claude.com/en/docs/claude-code/overview) " +
|
|
1313
|
-
"and retry.",
|
|
1314
|
-
);
|
|
1315
|
-
}
|
|
1316
|
-
return onPath;
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
/**
|
|
1320
|
-
* Headless session wrapper for Claude stages. Uses the Agent SDK's `query()`
|
|
1321
|
-
* directly instead of tmux pane operations. Implements the same `query()`
|
|
1322
|
-
* interface as {@link ClaudeSessionWrapper} so workflow callbacks work
|
|
1323
|
-
* identically for headless and interactive stages.
|
|
1324
|
-
*
|
|
1325
|
-
* The `query()` method accepts the full Agent SDK parameter types —
|
|
1326
|
-
* `prompt` can be a plain string or an `AsyncIterable<SDKUserMessage>`
|
|
1327
|
-
* for multi-turn streaming, and `options` passes through SDK-level
|
|
1328
|
-
* configuration (abort controllers, allowed tools, agents, etc.).
|
|
1329
|
-
*/
|
|
1330
|
-
export class HeadlessClaudeSessionWrapper {
|
|
1331
|
-
readonly paneId = "";
|
|
1332
|
-
/**
|
|
1333
|
-
* Project root the workflow is operating against. Used to resolve
|
|
1334
|
-
* project-scoped config (e.g. `additional-instructions`) against the
|
|
1335
|
-
* workflow's actual root rather than `process.cwd()`, which can drift
|
|
1336
|
-
* when workflows are invoked programmatically or from a subdirectory.
|
|
1337
|
-
*/
|
|
1338
|
-
private readonly _projectRoot: string;
|
|
1339
|
-
/**
|
|
1340
|
-
* The Claude session UUID of the most recently completed `query()`. Exposed
|
|
1341
|
-
* via `s.sessionId` so workflows can pass it to `s.save(s.sessionId)` and
|
|
1342
|
-
* have the save path read the correct transcript, even when several headless
|
|
1343
|
-
* Claude stages run in parallel (each call gets its own SDK-assigned UUID).
|
|
1344
|
-
*/
|
|
1345
|
-
private _lastSessionId: string = "";
|
|
1346
|
-
|
|
1347
|
-
constructor(projectRoot: string) {
|
|
1348
|
-
this._projectRoot = projectRoot;
|
|
1349
|
-
}
|
|
1350
|
-
/**
|
|
1351
|
-
* Validated structured output captured from the most recent `query()`'s
|
|
1352
|
-
* `result` message. Populated only when callers pass
|
|
1353
|
-
* `options.outputFormat = { type: "json_schema", schema }` and the SDK
|
|
1354
|
-
* produced a `subtype: "success"` result with `structured_output` attached.
|
|
1355
|
-
* Remains `undefined` on plain text runs or when the SDK fails validation
|
|
1356
|
-
* (`error_max_structured_output_retries`).
|
|
1357
|
-
*/
|
|
1358
|
-
private _lastStructuredOutput: unknown = undefined;
|
|
1359
|
-
|
|
1360
|
-
get sessionId(): string {
|
|
1361
|
-
return this._lastSessionId;
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
get lastStructuredOutput(): unknown {
|
|
1365
|
-
return this._lastStructuredOutput;
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
async query(
|
|
1369
|
-
prompt: string | AsyncIterable<SDKUserMessage>,
|
|
1370
|
-
options?: Partial<SDKOptions>,
|
|
1371
|
-
): Promise<SessionMessage[]> {
|
|
1372
|
-
// Auto-deny the `AskUserQuestion` tool in headless runs. Without this, the
|
|
1373
|
-
// agent can call it and the SDK query will sit blocked forever since no
|
|
1374
|
-
// human is attached to answer.
|
|
1375
|
-
const sdkOpts = options ?? {};
|
|
1376
|
-
const additional = await resolveAdditionalInstructionsContent(this._projectRoot);
|
|
1377
|
-
const headlessSdkOpts: Partial<SDKOptions> = {
|
|
1378
|
-
...sdkOpts,
|
|
1379
|
-
pathToClaudeCodeExecutable:
|
|
1380
|
-
sdkOpts.pathToClaudeCodeExecutable ?? resolveHeadlessClaudeBin(),
|
|
1381
|
-
disallowedTools: mergeDisallowedTools(sdkOpts.disallowedTools, [
|
|
1382
|
-
"AskUserQuestion",
|
|
1383
|
-
]),
|
|
1384
|
-
...(additional
|
|
1385
|
-
? { systemPrompt: mergeSystemPromptAppend(sdkOpts.systemPrompt, additional) }
|
|
1386
|
-
: {}),
|
|
1387
|
-
};
|
|
1388
|
-
|
|
1389
|
-
let sdkSessionId = "";
|
|
1390
|
-
let structuredOutput: unknown = undefined;
|
|
1391
|
-
try {
|
|
1392
|
-
await withAtomicTempEnv(async () => {
|
|
1393
|
-
for await (const msg of sdkQuery({ prompt, options: headlessSdkOpts })) {
|
|
1394
|
-
if (msg.type === "result") {
|
|
1395
|
-
const record = msg as Record<string, unknown>;
|
|
1396
|
-
sdkSessionId = String(record.session_id ?? "");
|
|
1397
|
-
if (record.subtype === "success" && "structured_output" in record) {
|
|
1398
|
-
structuredOutput = record.structured_output;
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
});
|
|
1403
|
-
} catch (err) {
|
|
1404
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1405
|
-
throw new Error(`Claude SDK query failed: ${detail}`);
|
|
1406
|
-
}
|
|
1407
|
-
if (!sdkSessionId) {
|
|
1408
|
-
throw new Error(
|
|
1409
|
-
"Claude SDK query completed without a `result` message — " +
|
|
1410
|
-
"likely a stream idle timeout, aborted request, or upstream API error. " +
|
|
1411
|
-
"Set CLAUDE_ENABLE_STREAM_WATCHDOG=1 (and tune CLAUDE_STREAM_IDLE_TIMEOUT_MS / " +
|
|
1412
|
-
"API_TIMEOUT_MS) so the CLI surfaces a concrete failure instead of exiting silently.",
|
|
1413
|
-
);
|
|
1414
|
-
}
|
|
1415
|
-
this._lastSessionId = sdkSessionId;
|
|
1416
|
-
this._lastStructuredOutput = structuredOutput;
|
|
1417
|
-
return getSessionMessages(sdkSessionId, { dir: process.cwd() });
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
async disconnect(): Promise<void> {}
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
// ---------------------------------------------------------------------------
|
|
1424
|
-
// Static source validation
|
|
1425
|
-
// ---------------------------------------------------------------------------
|
|
1426
|
-
|
|
1427
|
-
import { createProviderValidator } from "../types.ts";
|
|
1428
|
-
|
|
1429
|
-
/**
|
|
1430
|
-
* Validate a Claude workflow source file for common mistakes.
|
|
1431
|
-
*
|
|
1432
|
-
* Warns on direct usage of createClaudeSession/claudeQuery — the runtime
|
|
1433
|
-
* now handles init/cleanup automatically via s.client and s.session.
|
|
1434
|
-
*/
|
|
1435
|
-
export const validateClaudeWorkflow = createProviderValidator([
|
|
1436
|
-
{
|
|
1437
|
-
pattern: /\bcreateClaudeSession\b/,
|
|
1438
|
-
rule: "claude/manual-session",
|
|
1439
|
-
message:
|
|
1440
|
-
"Manual createClaudeSession() call detected. The runtime auto-starts the Claude CLI — " +
|
|
1441
|
-
"use s.session.query() instead of claudeQuery(). Pass chatFlags via the second arg to ctx.stage().",
|
|
1442
|
-
},
|
|
1443
|
-
{
|
|
1444
|
-
pattern: /\bclaudeQuery\b/,
|
|
1445
|
-
rule: "claude/manual-query",
|
|
1446
|
-
message:
|
|
1447
|
-
"Direct claudeQuery() call detected. Use s.session.query(prompt) instead — " +
|
|
1448
|
-
"it wraps claudeQuery with the correct paneId.",
|
|
1449
|
-
},
|
|
1450
|
-
]);
|