@yeyuan98/opencode-bioresearcher-plugin 1.6.5 → 1.6.6

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/README.md CHANGED
@@ -137,6 +137,7 @@ Plugin-shipped skills are automatically copied into `.opencode/skills/` at plugi
137
137
  - `bioresearcher-core`: core patterns and utilities (retry, JSON tools, subagent waves) for skill development.
138
138
  - `env-jsonc-setup`: guided setup for database connection configuration (db-tools).
139
139
  - `gromacs-guides`: reusable guides for GROMACS molecular dynamics workflows.
140
+ - `pzfx-io`: read, parse, write, and convert GraphPad Prism PZFX files (inspect, edit, add-column, CSV/TSV/JSON round-trips).
140
141
  - `bioresearcher-tests`: comprehensive test suite for plugin tools and skills.
141
142
 
142
143
  Prompt the following and follow along:
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: pzfx-io
3
+ description: Read, parse, write, and convert GraphPad Prism PZFX files
4
+ allowedTools:
5
+ - Read
6
+ - Bash
7
+ ---
8
+
9
+ # PZFX I/O
10
+
11
+ This skill provides tools for working with GraphPad Prism PZFX files.
12
+
13
+ ## Quick Start
14
+
15
+ ### Step 1: Load This Skill
16
+ The skill is loaded automatically when agent calls `skill pzfx-io`.
17
+
18
+ ### Step 2: Extract Skill Path
19
+ From the `<skill_files>` section in the skill tool output, extract the `<skill_path>` value.
20
+
21
+ ### Step 3: Read the Relevant Guide
22
+ ```
23
+ Read <skill_path>/guides/schema-reference.md
24
+ Read <skill_path>/guides/read-parse.md
25
+ Read <skill_path>/guides/write-edit.md
26
+ Read <skill_path>/guides/convert.md
27
+ ```
28
+
29
+ ## Available Commands
30
+
31
+ All commands are invoked via:
32
+ ```bash
33
+ uv run python <skill_path>/pzfx.py <command> [options]
34
+ ```
35
+
36
+ | Command | Purpose |
37
+ |---------|---------|
38
+ | `inspect` | Show file metadata (tables, columns, types) |
39
+ | `read` | Extract table data to stdout (JSON/CSV/TSV) |
40
+ | `write` | Create new PZFX file from CSV/TSV/JSON data |
41
+ | `edit` | Modify cell values in an existing PZFX file |
42
+ | `add-column` | Add a new column to an existing table |
43
+ | `convert` | Convert PZFX to CSV/TSV/JSON files |
44
+
45
+ ## Available Guides
46
+
47
+ | Task | Guide |
48
+ |------|-------|
49
+ | PZFX XML schema details | `guides/schema-reference.md` |
50
+ | Reading and parsing files | `guides/read-parse.md` |
51
+ | Writing and editing files | `guides/write-edit.md` |
52
+ | Converting to other formats | `guides/convert.md` |
53
+
54
+ ## Guide Summaries
55
+
56
+ ### schema-reference.md
57
+ Complete PZFX XML schema documentation:
58
+ - File structure and XML style variants (Prism 6-7 vs 8+)
59
+ - Table/Column element attributes and valid values
60
+ - Cell data formats, excluded values, comma decimals
61
+ - Validation rules per table type
62
+
63
+ ### read-parse.md
64
+ How to inspect and read PZFX data:
65
+ - `inspect` for metadata overview
66
+ - `read` for data extraction (JSON, CSV, TSV)
67
+ - Excluded data handling modes (keep, exclude, star)
68
+ - Value normalization rules
69
+ - Multi-table and HugeTable handling
70
+
71
+ ### write-edit.md
72
+ Creating and modifying PZFX files:
73
+ - `write` to create new files from CSV/TSV/JSON
74
+ - `edit` to modify cell values (with exclusion flags)
75
+ - `add-column` to append new columns
76
+ - Dry-run mode for validation
77
+ - Template preservation and round-trip safety
78
+
79
+ ### convert.md
80
+ Converting PZFX to other formats:
81
+ - CSV, TSV, JSON output formats
82
+ - Single table or batch conversion
83
+ - Custom output paths
84
+ - Comparison with `read` command
85
+
86
+ ## Common Workflows
87
+
88
+ ### Inspect a file
89
+ ```bash
90
+ uv run python <skill_path>/pzfx.py inspect <file.pzfx>
91
+ ```
92
+
93
+ ### Read data as CSV
94
+ ```bash
95
+ uv run python <skill_path>/pzfx.py read <file.pzfx> --format csv --include-x --include-row-titles
96
+ ```
97
+
98
+ ### Edit a column
99
+ ```bash
100
+ uv run python <skill_path>/pzfx.py edit <file.pzfx> --column-title "Col" --subcolumn 0 --values '[1,2,3]'
101
+ ```
102
+
103
+ ### Convert to CSV file
104
+ ```bash
105
+ uv run python <skill_path>/pzfx.py convert <file.pzfx> --format csv
106
+ ```
107
+
108
+ ### Create new file from CSV
109
+ ```bash
110
+ uv run python <skill_path>/pzfx.py write output.pzfx --data-file data.csv --data-format csv --table-type OneWay
111
+ ```
@@ -0,0 +1,102 @@
1
+ # Converting PZFX Files
2
+
3
+ ## Convert to CSV
4
+
5
+ ```bash
6
+ uv run python pzfx.py convert <file.pzfx> --format csv
7
+ ```
8
+
9
+ Creates `<basename>.csv` next to the original file.
10
+
11
+ ## Convert to TSV
12
+
13
+ ```bash
14
+ uv run python pzfx.py convert <file.pzfx> --format tsv
15
+ ```
16
+
17
+ ## Convert to JSON
18
+
19
+ ```bash
20
+ uv run python pzfx.py convert <file.pzfx> --format json
21
+ ```
22
+
23
+ JSON output includes full structured data:
24
+ ```json
25
+ {
26
+ "success": true,
27
+ "table_id": "Table0",
28
+ "table_type": "OneWay",
29
+ "columns": [...],
30
+ "row_titles": [...],
31
+ "source_file": "/path/to/file.pzfx"
32
+ }
33
+ ```
34
+
35
+ ## Convert Specific Table
36
+
37
+ For multi-table files, convert a single table:
38
+ ```bash
39
+ uv run python pzfx.py convert <file.pzfx> --table 0 --format csv
40
+ ```
41
+
42
+ ## Convert All Tables
43
+
44
+ Without `--table`, all tables are converted. For CSV/TSV, each table gets a separate file:
45
+ ```
46
+ basename_Table0.csv
47
+ basename_Table1.csv
48
+ ```
49
+
50
+ ## Custom Output Path
51
+
52
+ ```bash
53
+ uv run python pzfx.py convert <file.pzfx> --format csv --output /path/to/output.csv
54
+ ```
55
+
56
+ Note: `--output` is only used when converting a single table (or the last table).
57
+
58
+ ## Excluded Data Modes
59
+
60
+ ```bash
61
+ uv run python pzfx.py convert <file.pzfx> --exclude-mode keep
62
+ uv run python pzfx.py convert <file.pzfx> --exclude-mode star
63
+ ```
64
+
65
+ | Mode | Effect |
66
+ |------|--------|
67
+ | `exclude` (default) | Excluded values omitted (empty cells) |
68
+ | `keep` | Excluded values preserved as raw numbers |
69
+ | `star` | Excluded values marked with asterisk (e.g., `90*`) |
70
+
71
+ ## Convert Output Includes
72
+
73
+ By default, converted output includes:
74
+ - Row titles (if present)
75
+ - X column data (if present, for XY/Survival tables)
76
+ - All Y column subcolumns with proper naming
77
+
78
+ Column naming follows YFormat conventions:
79
+ - Replicates: `Title_1`, `Title_2`
80
+ - SD: `Title_MEAN`, `Title_SD`
81
+ - SE: `Title_MEAN`, `Title_SE`
82
+ - etc.
83
+
84
+ ## Batch Conversion Example
85
+
86
+ Convert all PZFX files in a directory:
87
+ ```bash
88
+ for f in *.pzfx; do
89
+ uv run python pzfx.py convert "$f" --format csv
90
+ done
91
+ ```
92
+
93
+ ## Comparison with `read` Command
94
+
95
+ | Feature | `read` | `convert` |
96
+ |---------|--------|-----------|
97
+ | Output to stdout | Yes | No (writes file) |
98
+ | Write to file | No | Yes |
99
+ | Include row titles | `--include-row-titles` | Always included |
100
+ | Include X column | `--include-x` | Always included |
101
+ | Multi-table | One at a time (`--table`) | All or one |
102
+ | Use case | Quick inspection, piping | File export, batch processing |
@@ -0,0 +1,116 @@
1
+ # Reading and Parsing PZFX Files
2
+
3
+ ## Quick Inspect
4
+
5
+ Get file metadata without extracting data:
6
+
7
+ ```bash
8
+ uv run python pzfx.py inspect <file.pzfx>
9
+ ```
10
+
11
+ Output includes: table IDs, types, formats, row counts, column titles and subcolumn counts.
12
+
13
+ ## Read Table Data
14
+
15
+ ### JSON format (default)
16
+ ```bash
17
+ uv run python pzfx.py read <file.pzfx> --format json --include-x --include-row-titles
18
+ ```
19
+
20
+ JSON structure:
21
+ ```json
22
+ {
23
+ "table_id": "Table0",
24
+ "table_type": "XY",
25
+ "columns": [
26
+ {
27
+ "title": "X Title",
28
+ "subcolumn_names": ["X Title"],
29
+ "data": [[1.0, 2.0, 3.0]]
30
+ },
31
+ {
32
+ "title": "Sample",
33
+ "subcolumn_names": ["Sample_1", "Sample_2"],
34
+ "data": [[100.0, 90.0, 80.0], [50.0, 60.0, 70.0]]
35
+ }
36
+ ],
37
+ "row_titles": ["A", "B", "C"]
38
+ }
39
+ ```
40
+
41
+ ### CSV/TSV format
42
+ ```bash
43
+ uv run python pzfx.py read <file.pzfx> --format csv --include-x --include-row-titles
44
+ uv run python pzfx.py read <file.pzfx> --format tsv --include-x
45
+ ```
46
+
47
+ Output:
48
+ ```
49
+ ROWTITLE,XX,Ya_1,Ya_2,Yb_1,Yb_2
50
+ A,1.0,100.0,50.0,1.0,10.0
51
+ B,2.0,90.0,60.0,2.0,9.0
52
+ C,3.0,80.0,70.0,3.0,8.0
53
+ ```
54
+
55
+ ## Multi-Table Files
56
+
57
+ Specify table index (0-based):
58
+ ```bash
59
+ uv run python pzfx.py read <file.pzfx> --table 1 --format csv
60
+ ```
61
+
62
+ Use `inspect` first to see how many tables exist.
63
+
64
+ ## Excluded Data Handling
65
+
66
+ Prism supports "excluding" (striking through) data points. Control with `--exclude-mode`:
67
+
68
+ | Mode | Effect | Example |
69
+ |------|--------|---------|
70
+ | `exclude` (default) | Excluded values → `null`/empty | `[100, null, 80]` |
71
+ | `keep` | Excluded values kept as-is | `[100, 90, 80]` |
72
+ | `star` | Append asterisk to excluded | `[100, "90*", 80]` |
73
+
74
+ ```bash
75
+ uv run python pzfx.py read <file.pzfx> --exclude-mode star --format csv
76
+ ```
77
+
78
+ ## Value Normalization
79
+
80
+ Values are automatically normalized:
81
+ - `"42."` → integer `42`
82
+ - `"3.14"` → float `3.14`
83
+ - `"3,5"` → float `3.5` (comma-decimal notation)
84
+ - `""` or empty → `null`
85
+ - Non-numeric text → kept as string
86
+
87
+ ## Reading Specific Table Types
88
+
89
+ ### XY Tables (with X column)
90
+ ```bash
91
+ uv run python pzfx.py read <file.pzfx> --include-x --format csv
92
+ ```
93
+
94
+ ### Contingency Tables (with row titles)
95
+ ```bash
96
+ uv run python pzfx.py read <file.pzfx> --include-row-titles --format csv
97
+ ```
98
+
99
+ ### Date-Formatted X Columns
100
+ XColumn stores fractional years; XAdvancedColumn stores formatted date strings. Both are included with `--include-x`.
101
+
102
+ ### HugeTable Files
103
+ Handled identically to regular Tables. `inspect` shows `"is_huge": true`.
104
+
105
+ ## Error Output Format
106
+
107
+ On failure, JSON error is returned:
108
+ ```json
109
+ {
110
+ "success": false,
111
+ "error": "CATEGORY: message",
112
+ "hint": "Suggested fix"
113
+ }
114
+ ```
115
+
116
+ Error categories: `FILE_NOT_FOUND`, `PARSE_ERROR`, `VALIDATION_ERROR`
@@ -0,0 +1,167 @@
1
+ # PZFX Schema Reference
2
+
3
+ ## File Structure
4
+
5
+ Every PZFX file follows this layout:
6
+
7
+ ```xml
8
+ <?xml version="1.0" encoding="UTF-8"?>
9
+ <GraphPadPrismFile PrismXMLVersion="5.00">
10
+ <Created>...</Created>
11
+ <InfoSequence><Ref ID="Info0" Selected="1"></Ref></InfoSequence>
12
+ <Info ID="Info0">...</Info>
13
+ <TableSequence><Ref ID="Table0" Selected="1"></Ref></TableSequence>
14
+ <Table ...>...</Table>
15
+ <!--Analyses, graphs and layouts as compressed binary. Don't edit this part of the file.-->
16
+ <Template dt:dt="bin.base64" xmlns:dt="urn:schemas-microsoft-com:datatypes">base64...</Template>
17
+ </GraphPadPrismFile>
18
+ ```
19
+
20
+ The `<Template>` block is an opaque base64-encoded zlib blob containing graphs, analyses, and layouts. It must never be modified.
21
+
22
+ ## XML Style Variants
23
+
24
+ | Variant | Prism Version | Features |
25
+ |---------|---------------|----------|
26
+ | Classic | 6-7 | Closing tags (`</Subcolumn></YColumn>`), no xmlns |
27
+ | Modern | 8+ | Self-closing tags (`<Subcolumn/>`), `xmlns="http://graphpad.com/prism/Prism.htm"` on root |
28
+
29
+ Both variants are handled by stripping XML namespaces at parse time.
30
+
31
+ ## Table Element Attributes
32
+
33
+ ```xml
34
+ <Table ID="Table0" XFormat="numbers" YFormat="replicates" Replicates="2"
35
+ TableType="XY" EVFormat="AsteriskAfterNumber">
36
+ ```
37
+
38
+ | Attribute | Required | Values |
39
+ |-----------|----------|--------|
40
+ | ID | Yes | Unique table identifier (e.g. "Table0") |
41
+ | TableType | Yes | `OneWay`, `TwoWay`, `XY`, `Survival`, `Contingency`, `PartsOfWhole` |
42
+ | XFormat | Yes | `none`, `numbers`, `date`, `error` |
43
+ | YFormat | Conditional | `replicates`, `SD`, `SE`, `CV`, `SDN`, `SEN`, `CVN`, `low-high`, `upper-lower-limits` |
44
+ | Replicates | Conditional | Integer, required when YFormat="replicates" |
45
+ | EVFormat | Yes | Always `AsteriskAfterNumber` |
46
+
47
+ ### TableType to XFormat Mapping
48
+
49
+ | TableType | Required XFormat |
50
+ |-----------|-----------------|
51
+ | OneWay | none |
52
+ | TwoWay | none |
53
+ | Contingency | none |
54
+ | PartsOfWhole | none |
55
+ | XY | numbers (or date, error) |
56
+ | Survival | numbers |
57
+
58
+ ## Column Types
59
+
60
+ ### XColumn (XY/Survival tables only)
61
+
62
+ ```xml
63
+ <XColumn Width="199" Subcolumns="1" Decimals="8">
64
+ <Title>X Title</Title>
65
+ <Subcolumn>
66
+ <d>1.0</d>
67
+ <d>2.0</d>
68
+ </Subcolumn>
69
+ </XColumn>
70
+ ```
71
+
72
+ - When XFormat="error", has 2 subcolumns (value ± error)
73
+ - Subcolumns count is always 1 for numbers/date formats
74
+
75
+ ### XAdvancedColumn (Date-formatted X columns)
76
+
77
+ ```xml
78
+ <XAdvancedColumn Version="1" Width="199" Decimals="8" Subcolumns="1">
79
+ <Title>Date X</Title>
80
+ <Subcolumn>
81
+ <d>2023-01-15</d>
82
+ </Subcolumn>
83
+ </XAdvancedColumn>
84
+ ```
85
+
86
+ - Stores formatted date strings alongside XColumn fractional years
87
+ - Always paired with an XColumn when XFormat="date"
88
+
89
+ ### YColumn
90
+
91
+ ```xml
92
+ <YColumn Width="89" Decimals="0" Subcolumns="2">
93
+ <Title>Sample</Title>
94
+ <Subcolumn><d>1.</d><d>2.</d></Subcolumn>
95
+ <Subcolumn><d>3.</d><d>4.</d></Subcolumn>
96
+ </YColumn>
97
+ ```
98
+
99
+ Subcolumn count depends on YFormat:
100
+
101
+ | YFormat | Subcolumns | Naming Convention |
102
+ |---------|------------|-------------------|
103
+ | replicates | = Replicates | `{Title}_1`, `{Title}_2`, ... |
104
+ | SD | 2 | `{Title}_MEAN`, `{Title}_SD` |
105
+ | SE | 2 | `{Title}_MEAN`, `{Title}_SE` |
106
+ | CV | 2 | `{Title}_MEAN`, `{Title}_CV` |
107
+ | SDN | 3 | `{Title}_MEAN`, `{Title}_SD`, `{Title}_N` |
108
+ | SEN | 3 | `{Title}_MEAN`, `{Title}_SEM`, `{Title}_N` |
109
+ | CVN | 3 | `{Title}_MEAN`, `{Title}_CV`, `{Title}_N` |
110
+ | low-high | 3 | `{Title}_MEAN`, `{Title}_PLUSERROR`, `{Title}_MINUSERROR` |
111
+ | upper-lower-limits | 3 | `{Title}_MEAN`, `{Title}_UPPERLIMIT`, `{Title}_LOWERLIMIT` |
112
+
113
+ ### RowTitlesColumn (Contingency tables)
114
+
115
+ ```xml
116
+ <RowTitlesColumn Width="81">
117
+ <Subcolumn>
118
+ <d>Male</d>
119
+ <d>Female</d>
120
+ </Subcolumn>
121
+ </RowTitlesColumn>
122
+ ```
123
+
124
+ ## Cell Data (`<d>` Elements)
125
+
126
+ ```xml
127
+ <d>42.5</d> <!-- Normal value -->
128
+ <d>3,5</d> <!-- Comma decimal (3.5) -->
129
+ <d Excluded="1">99.0</d> <!-- Excluded/strikethrough value -->
130
+ <d></d> <!-- Empty cell -->
131
+ <d><TextAlign>Label</TextAlign></d> <!-- Text cell (non-numeric) -->
132
+ ```
133
+
134
+ - Comma-as-decimal: values like `3,5` are normalized to `3.5`
135
+ - Excluded cells have `Excluded="1"` attribute
136
+ - Empty `<d></d>` or `<d/>` represents missing data
137
+
138
+ ## Other Elements
139
+
140
+ ### Info Block
141
+ ```xml
142
+ <Info ID="Info0">
143
+ <Title>Project info 1</Title>
144
+ <Notes></Notes>
145
+ <Constant><Name>Experiment Date</Name><Value>2024-01-01</Value></Constant>
146
+ </Info>
147
+ ```
148
+
149
+ ### HugeTable
150
+ Identical to `Table` internally, just uses `<HugeTable>` tag for large datasets.
151
+
152
+ ### FloatingNote
153
+ ```xml
154
+ <FloatingNote ID="#0" Width="262" Height="137" Left="451" Top="33" Expanded="1">
155
+ <Title>Note</Title>
156
+ <Content>Some annotation text</Content>
157
+ </FloatingNote>
158
+ ```
159
+
160
+ ## Validation Rules
161
+
162
+ 1. PartsOfWhole tables must have exactly 1 YColumn
163
+ 2. Contingency tables must have a RowTitlesColumn
164
+ 3. YColumn titles must be unique within a table
165
+ 4. SD/SE/CV format requires exactly 2 subcolumns per YColumn
166
+ 5. SDN/SEN/CVN/low-high/upper-lower-limits requires exactly 3 subcolumns
167
+ 6. XY tables require XFormat in (numbers, date, error)
@@ -0,0 +1,150 @@
1
+ # Writing and Editing PZFX Files
2
+
3
+ ## Creating New PZFX Files
4
+
5
+ ### From CSV/TSV data
6
+ ```bash
7
+ uv run python pzfx.py write output.pzfx \
8
+ --data-file data.csv \
9
+ --data-format csv \
10
+ --table-type OneWay
11
+ ```
12
+
13
+ ### From TSV data
14
+ ```bash
15
+ uv run python pzfx.py write output.pzfx \
16
+ --data-file data.tsv \
17
+ --data-format tsv \
18
+ --table-type XY \
19
+ --x-format numbers \
20
+ --y-format replicates \
21
+ --replicates 2
22
+ ```
23
+
24
+ ### From JSON data
25
+ ```bash
26
+ uv run python pzfx.py write output.pzfx \
27
+ --data-file data.json \
28
+ --data-format json \
29
+ --table-type TwoWay \
30
+ --y-format SD
31
+ ```
32
+
33
+ ### Input CSV/TSV Format
34
+
35
+ Headers determine column assignment. For XY tables, the first non-ROWTITLE column becomes the X column:
36
+
37
+ ```csv
38
+ ROWTITLE,Xconc,Ya,Yb
39
+ A,1.0,100,50
40
+ B,2.0,90,60
41
+ C,3.0,80,70
42
+ ```
43
+
44
+ For non-XY tables (XFormat=none), all columns become YColumns:
45
+
46
+ ```csv
47
+ Aa,Bb
48
+ 1,100
49
+ 2,90
50
+ 3,80
51
+ ```
52
+
53
+ ### Dry Run (validate without writing)
54
+ ```bash
55
+ uv run python pzfx.py write output.pzfx \
56
+ --data-file data.csv --data-format csv \
57
+ --table-type OneWay --dry-run
58
+ ```
59
+
60
+ ## Editing Existing Files
61
+
62
+ ### Modify cell values
63
+ ```bash
64
+ uv run python pzfx.py edit <file.pzfx> \
65
+ --column-title "Aa" \
66
+ --subcolumn 0 \
67
+ --values '[10,20,30,40]'
68
+ ```
69
+
70
+ ### Edit with exclusion flags
71
+ ```bash
72
+ uv run python pzfx.py edit <file.pzfx> \
73
+ --column-title "Sample" \
74
+ --subcolumn 0 \
75
+ --values '[100,90,80]' \
76
+ --exclude-values '[false,true,false]'
77
+ ```
78
+
79
+ ### Edit and save to different file
80
+ ```bash
81
+ uv run python pzfx.py edit <file.pzfx> \
82
+ --column-title "Aa" \
83
+ --subcolumn 0 \
84
+ --values '[10,20,30,40]' \
85
+ --output modified.pzfx
86
+ ```
87
+
88
+ ### Preview changes without writing
89
+ ```bash
90
+ uv run python pzfx.py edit <file.pzfx> \
91
+ --column-title "Aa" \
92
+ --subcolumn 0 \
93
+ --values '[10,20,30,40]' \
94
+ --dry-run
95
+ ```
96
+
97
+ ## Adding Columns
98
+
99
+ ### Add a single-subcolumn column
100
+ ```bash
101
+ uv run python pzfx.py add-column <file.pzfx> \
102
+ --title "NewCol" \
103
+ --subcolumns 1 \
104
+ --values '[[1,2,3,4]]' \
105
+ --output modified.pzfx
106
+ ```
107
+
108
+ ### Add a multi-subcolumn column (e.g., Mean ± SD)
109
+ ```bash
110
+ uv run python pzfx.py add-column <file.pzfx> \
111
+ --title "Treatment" \
112
+ --subcolumns 2 \
113
+ --values '[[100,110,120],[10,20,30]]' \
114
+ --output modified.pzfx
115
+ ```
116
+
117
+ The `--values` is a JSON array of arrays: one inner array per subcolumn.
118
+
119
+ ### Dry run
120
+ ```bash
121
+ uv run python pzfx.py add-column <file.pzfx> \
122
+ --title "NewCol" --subcolumns 1 \
123
+ --values '[[1,2,3]]' --dry-run
124
+ ```
125
+
126
+ ## Table Type Constraints
127
+
128
+ | TableType | XFormat | YColumns | Special |
129
+ |-----------|---------|----------|---------|
130
+ | OneWay | none | >= 1 | Default column type |
131
+ | TwoWay | none | >= 1 | Supports error formats |
132
+ | XY | numbers/date/error | >= 1 | Requires XColumn |
133
+ | Survival | numbers | >= 1 | X = time, Y = event |
134
+ | Contingency | none | >= 1 | Requires RowTitlesColumn |
135
+ | PartsOfWhole | none | exactly 1 | Pie chart data |
136
+
137
+ ## Template Preservation
138
+
139
+ All write/edit operations preserve the original `<Template>` base64 blob unchanged. This ensures graphs, analyses, and layouts survive round-trips.
140
+
141
+ ## Round-Trip Safety
142
+
143
+ Read → edit → write preserves:
144
+ - All columns not being edited
145
+ - XColumn and XAdvancedColumn data
146
+ - RowTitlesColumn data
147
+ - FloatingNote annotations
148
+ - Table metadata (ID, type, format, replicates)
149
+ - The complete Template blob
150
+ - Info block data
@@ -0,0 +1,6 @@
1
+ [project]
2
+ name = "pzfx-io-skill"
3
+ version = "0.1.0"
4
+ description = "GraphPad Prism PZFX file read/write skill"
5
+ requires-python = ">=3.10"
6
+ dependencies = []