qcoder 0.5.0a5__tar.gz → 0.5.0a7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/CHANGELOG.md +24 -0
  2. {qcoder-0.5.0a5/src/qcoder.egg-info → qcoder-0.5.0a7}/PKG-INFO +23 -1
  3. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/README.md +20 -0
  4. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/pyproject.toml +3 -1
  5. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/__init__.py +1 -1
  6. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/cli.py +131 -28
  7. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/core/run_config.py +6 -0
  8. qcoder-0.5.0a7/src/qcoder/core/share_safe.py +278 -0
  9. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/context/bundle.py +2 -1
  10. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/context/markdown.py +7 -1
  11. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/qasm2_regex_parser.py +1 -1
  12. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/review/bundle.py +4 -1
  13. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/review/markdown.py +7 -1
  14. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/explorer/derived_evidence.py +1 -1
  15. qcoder-0.5.0a7/src/qcoder/mcp/__init__.py +5 -0
  16. qcoder-0.5.0a7/src/qcoder/mcp/server.py +87 -0
  17. qcoder-0.5.0a7/src/qcoder/mcp/tools.py +334 -0
  18. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pipelines/analyze.py +12 -1
  19. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pipelines/context.py +4 -1
  20. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pipelines/review.py +14 -2
  21. {qcoder-0.5.0a5 → qcoder-0.5.0a7/src/qcoder.egg-info}/PKG-INFO +23 -1
  22. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder.egg-info/SOURCES.txt +4 -0
  23. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/LICENSE +0 -0
  24. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/MANIFEST.in +0 -0
  25. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/NOTICE +0 -0
  26. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/setup.cfg +0 -0
  27. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/__main__.py +0 -0
  28. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/core/__init__.py +0 -0
  29. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/core/context.py +0 -0
  30. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/core/qasm2/__init__.py +0 -0
  31. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/core/qasm2/adjoint_eligibility.py +0 -0
  32. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/core/qasm2/mirror_build.py +0 -0
  33. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/core/schema.py +0 -0
  34. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/context/__init__.py +0 -0
  35. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/adapters/__init__.py +0 -0
  36. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/adapters/cirq_intake.py +0 -0
  37. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/adapters/pennylane_intake.py +0 -0
  38. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/adapters/qiskit_intake.py +0 -0
  39. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/extractor.py +0 -0
  40. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/features/compute_v0.py +0 -0
  41. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/features/glossary_v0.py +0 -0
  42. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/features/schema_v0.py +0 -0
  43. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/ir.py +0 -0
  44. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/labeling.py +0 -0
  45. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/parsers/__init__.py +0 -0
  46. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/reps/cut_profile.py +0 -0
  47. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/reps/depth.py +0 -0
  48. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/reps/entangling_layers.py +0 -0
  49. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/reps/gate_set_stats.py +0 -0
  50. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/reps/interaction_graph.py +0 -0
  51. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/reps/interaction_graph_metrics.py +0 -0
  52. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/feature_extraction/reps/spans.py +0 -0
  53. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/guidance/__init__.py +0 -0
  54. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/guidance/model_pack.py +0 -0
  55. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/guidance/resource.py +0 -0
  56. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/guidance/structural_scores.py +0 -0
  57. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/profiles/__init__.py +0 -0
  58. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/profiles/feature_profiles_v0.py +0 -0
  59. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/review/__init__.py +0 -0
  60. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/review/counts_v0.py +0 -0
  61. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/engines/review/qiskit_counts.py +0 -0
  62. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/explorer/__init__.py +0 -0
  63. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/model_packs/__init__.py +0 -0
  64. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/model_packs/resource_guidance_local_v0.json +0 -0
  65. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pipelines/batch.py +0 -0
  66. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pro_preview/__init__.py +0 -0
  67. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pro_preview/client.py +0 -0
  68. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pro_preview/config.py +0 -0
  69. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pro_preview/errors.py +0 -0
  70. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/pro_preview/manifest.py +0 -0
  71. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder/tools/batch.py +0 -0
  72. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder.egg-info/dependency_links.txt +0 -0
  73. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder.egg-info/entry_points.txt +0 -0
  74. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder.egg-info/requires.txt +0 -0
  75. {qcoder-0.5.0a5 → qcoder-0.5.0a7}/src/qcoder.egg-info/top_level.txt +0 -0
@@ -1,6 +1,30 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## 0.5.0a7
5
+
6
+ - Add local read-only qCoder MCP for Cursor-oriented Explorer Launch workflows.
7
+ - Add MCP tools for local circuit analysis, context-pack generation, counts review, bounded findings, claim boundaries, next checks, and share-safe artifact sanitization.
8
+ - Document the Evidence-Grounded Coding Loop and preserve the manual artifact handoff as launch-required even when local MCP is available.
9
+ - Harden Windows-created inputs by accepting UTF-8 BOM in QASM and JSON paths used by local analysis/review.
10
+ - Improve `qcoder review` error guidance for malformed JSON and Qiskit counts input.
11
+ - Add MCP, share-safe, claim-boundary, and Windows/QASM/Qiskit tests plus release-check MCP stdio rehearsal.
12
+ - Preserve launch boundaries: no public hosted MCP claim, no live service calls from MCP, no token requirement, no autonomous code modification, no QPU/simulator submission, and no runtime/fidelity/backend/QPU/correctness/advantage claims.
13
+ - This is release-candidate preparation only until separately published.
14
+
15
+
16
+ ## 0.5.0a6
17
+
18
+ - Add share-safe / redacted artifact mode for artifacts intended for ChatGPT, Cursor, email, GitHub issues, or support threads.
19
+ - Add `--share-safe` / `--redact` to OSS artifact commands and Explorer evidence file output.
20
+ - Emit share-safety metadata in JSON artifacts: `share_safe`, `redactions_applied`, `raw_qasm_included`, `local_paths_included`, and `tokens_included`.
21
+ - Add a visible share-safe note to Markdown artifacts.
22
+ - Fix Windows free-text path redaction, including `C:\Users\...` paths, Windows forward-slash paths, UNC paths, home-relative paths, Linux home paths, and WSL-mounted Windows paths.
23
+ - Preserve normal rich local artifacts by default; share-safe mode is opt-in.
24
+ - Share-safe mode is designed for safer sharing and users should still review artifacts before sharing. It is not a privacy guarantee.
25
+ - Remaining P1 hardening: forward-slash UNC variants, broader Linux root patterns, independent post-scan hardening, and expanded docs wording.
26
+
27
+
4
28
  ## 0.5.0a5
5
29
 
6
30
  - Add `qcoder explorer evidence` as the primary Explorer Beta guided-evidence surface.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qcoder
3
- Version: 0.5.0a5
3
+ Version: 0.5.0a7
4
4
  Summary: Quantum circuit analysis and structured feature extraction tools.
5
5
  Author-email: Quantum Ready Solutions <support@qcoder.ai>
6
6
  Maintainer-email: Quantum Ready Solutions <support@qcoder.ai>
@@ -15,6 +15,8 @@ Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Topic :: Scientific/Engineering
16
16
  Classifier: Programming Language :: Python :: 3
17
17
  Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
18
20
  Requires-Python: >=3.11
19
21
  Description-Content-Type: text/markdown
20
22
  License-File: LICENSE
@@ -40,6 +42,7 @@ The current public local path is **qCoder OSS**. OSS commands run locally and do
40
42
  - `qcoder batch`
41
43
  - `qcoder context`
42
44
  - `qcoder review`
45
+ - `qcoder mcp` (local read-only Cursor MCP tools)
43
46
  - `qcoder explorer` (Explorer Beta account-backed status/demo/evidence checks)
44
47
  - `qcoder student` (temporary compatibility alias for Explorer Beta)
45
48
  - `qcoder pro` (archived pilot/bootstrap client contract; non-confidential local plumbing only, not a current public product path)
@@ -77,6 +80,24 @@ qcoder context path/to/circuit.qasm --out-json preflight.context.json --out-md p
77
80
  qcoder review --counts-json counts.json --format qiskit_counts --preflight-json preflight.context.json --out-json execution.review.json --out-md execution.review.md
78
81
  ```
79
82
 
83
+ For artifacts you intend to paste into ChatGPT, Cursor, email, GitHub issues, or support threads, add `--share-safe`:
84
+
85
+ ```bash
86
+ qcoder analyze path/to/circuit.qasm --json --share-safe
87
+ qcoder context path/to/circuit.qasm --out-json preflight.context.json --out-md preflight.context.md --share-safe
88
+ qcoder review --counts-json counts.json --format qiskit_counts --preflight-json preflight.context.json --out-json execution.review.json --out-md execution.review.md --share-safe
89
+ ```
90
+
91
+ Share-safe mode is designed for safer sharing: it redacts local paths and token/header-like strings, adds `share_safe=true`, and marks raw QASM/local paths/tokens as not included. Review artifacts before sharing; this is not a guarantee that all sensitive project content has been removed.
92
+
93
+ Local Cursor MCP:
94
+
95
+ ```bash
96
+ qcoder mcp serve
97
+ ```
98
+
99
+ The MCP server uses explicit user-selected local inputs only. It reads, inspects, explains, bounds, and recommends; it does not execute circuits, modify user code, read arbitrary files, call live services, or require tokens. See [Explorer Evidence-Grounded Coding Loop](examples/08_explorer_evidence_grounded_coding_loop.md) for the manual artifact loop, Cursor MCP setup, and claim boundaries.
100
+
80
101
  Explorer Beta compatibility checks and archived Pro bootstrap:
81
102
 
82
103
  ```bash
@@ -85,6 +106,7 @@ qcoder explorer demo
85
106
  qcoder explorer evidence
86
107
  qcoder explorer evidence --qasm path/to/circuit.qasm
87
108
  qcoder explorer evidence --context-json preflight.context.json
109
+ qcoder explorer evidence --qasm path/to/circuit.qasm --out-json explorer.json --out-md explorer.md --share-safe
88
110
  qcoder pro --help
89
111
  ```
90
112
 
@@ -10,6 +10,7 @@ The current public local path is **qCoder OSS**. OSS commands run locally and do
10
10
  - `qcoder batch`
11
11
  - `qcoder context`
12
12
  - `qcoder review`
13
+ - `qcoder mcp` (local read-only Cursor MCP tools)
13
14
  - `qcoder explorer` (Explorer Beta account-backed status/demo/evidence checks)
14
15
  - `qcoder student` (temporary compatibility alias for Explorer Beta)
15
16
  - `qcoder pro` (archived pilot/bootstrap client contract; non-confidential local plumbing only, not a current public product path)
@@ -47,6 +48,24 @@ qcoder context path/to/circuit.qasm --out-json preflight.context.json --out-md p
47
48
  qcoder review --counts-json counts.json --format qiskit_counts --preflight-json preflight.context.json --out-json execution.review.json --out-md execution.review.md
48
49
  ```
49
50
 
51
+ For artifacts you intend to paste into ChatGPT, Cursor, email, GitHub issues, or support threads, add `--share-safe`:
52
+
53
+ ```bash
54
+ qcoder analyze path/to/circuit.qasm --json --share-safe
55
+ qcoder context path/to/circuit.qasm --out-json preflight.context.json --out-md preflight.context.md --share-safe
56
+ qcoder review --counts-json counts.json --format qiskit_counts --preflight-json preflight.context.json --out-json execution.review.json --out-md execution.review.md --share-safe
57
+ ```
58
+
59
+ Share-safe mode is designed for safer sharing: it redacts local paths and token/header-like strings, adds `share_safe=true`, and marks raw QASM/local paths/tokens as not included. Review artifacts before sharing; this is not a guarantee that all sensitive project content has been removed.
60
+
61
+ Local Cursor MCP:
62
+
63
+ ```bash
64
+ qcoder mcp serve
65
+ ```
66
+
67
+ The MCP server uses explicit user-selected local inputs only. It reads, inspects, explains, bounds, and recommends; it does not execute circuits, modify user code, read arbitrary files, call live services, or require tokens. See [Explorer Evidence-Grounded Coding Loop](examples/08_explorer_evidence_grounded_coding_loop.md) for the manual artifact loop, Cursor MCP setup, and claim boundaries.
68
+
50
69
  Explorer Beta compatibility checks and archived Pro bootstrap:
51
70
 
52
71
  ```bash
@@ -55,6 +74,7 @@ qcoder explorer demo
55
74
  qcoder explorer evidence
56
75
  qcoder explorer evidence --qasm path/to/circuit.qasm
57
76
  qcoder explorer evidence --context-json preflight.context.json
77
+ qcoder explorer evidence --qasm path/to/circuit.qasm --out-json explorer.json --out-md explorer.md --share-safe
58
78
  qcoder pro --help
59
79
  ```
60
80
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qcoder"
7
- version = "0.5.0a5"
7
+ version = "0.5.0a7"
8
8
  description = "Quantum circuit analysis and structured feature extraction tools."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -23,6 +23,8 @@ classifiers = [
23
23
  "Topic :: Scientific/Engineering",
24
24
  "Programming Language :: Python :: 3",
25
25
  "Programming Language :: Python :: 3 :: Only",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
26
28
  ]
27
29
  dependencies = ["numpy"]
28
30
 
@@ -1,3 +1,3 @@
1
1
  __all__ = []
2
- __version__ = "0.5.0a5"
2
+ __version__ = "0.5.0a7"
3
3
  file = __file__
@@ -3,11 +3,17 @@ from __future__ import annotations
3
3
  import argparse
4
4
  import json
5
5
  import sys
6
+ from json import JSONDecodeError
6
7
  from pathlib import Path
7
8
 
8
9
  from qcoder.pipelines.analyze import analyze_qasm
9
10
  from qcoder.pipelines.context import write_preflight_context
10
11
  from qcoder.pipelines.review import write_execution_review
12
+ from qcoder.core.share_safe import (
13
+ make_share_safe_payload,
14
+ render_share_safe_note,
15
+ render_share_safe_provenance,
16
+ )
11
17
  from qcoder.explorer.derived_evidence import (
12
18
  ExplorerDerivedEvidenceRequestError,
13
19
  build_derived_evidence_request_from_context_json,
@@ -100,6 +106,13 @@ def _cmd_analyze(argv: list[str]) -> int:
100
106
  help="Include derived feature_profiles in JSON output (requires --json for analyze)",
101
107
  )
102
108
  p.add_argument("--json", action="store_true", help="Emit machine-readable JSON")
109
+ p.add_argument(
110
+ "--share-safe",
111
+ "--redact",
112
+ dest="share_safe",
113
+ action="store_true",
114
+ help="Redact local paths and sensitive runtime details in JSON output intended for sharing.",
115
+ )
103
116
  args = p.parse_args(argv)
104
117
 
105
118
  if args.profiles and not args.json:
@@ -120,9 +133,12 @@ def _cmd_analyze(argv: list[str]) -> int:
120
133
  )
121
134
 
122
135
  if args.json:
136
+ payload = report.to_json_dict(include_guidance=args.guidance, include_profiles=args.profiles)
137
+ if args.share_safe:
138
+ payload = make_share_safe_payload(payload)
123
139
  print(
124
140
  json.dumps(
125
- report.to_json_dict(include_guidance=args.guidance, include_profiles=args.profiles),
141
+ payload,
126
142
  indent=2,
127
143
  sort_keys=True,
128
144
  )
@@ -131,7 +147,9 @@ def _cmd_analyze(argv: list[str]) -> int:
131
147
 
132
148
  ex = report.example
133
149
  rc = report.run_config
134
- print(f"file: {ex.qasm_path}")
150
+ print(f"file: {'<redacted-local-path>' if args.share_safe else ex.qasm_path}")
151
+ if args.share_safe:
152
+ print("share_safe: true")
135
153
  print(f"format: {ex.ir.source_format}")
136
154
  if ex.name:
137
155
  print(f"name: {ex.name}")
@@ -218,6 +236,13 @@ def _cmd_context(argv: list[str]) -> int:
218
236
  action="store_true",
219
237
  help="Include full feature glossary/appendix in context artifacts (default: selected structural features only)",
220
238
  )
239
+ p.add_argument(
240
+ "--share-safe",
241
+ "--redact",
242
+ dest="share_safe",
243
+ action="store_true",
244
+ help="Write artifacts designed for safer sharing by redacting local paths and sensitive runtime details.",
245
+ )
221
246
  args = p.parse_args(argv)
222
247
 
223
248
  write_preflight_context(
@@ -229,9 +254,12 @@ def _cmd_context(argv: list[str]) -> int:
229
254
  include_full_features=args.full_features,
230
255
  circuit_id=args.circuit_id,
231
256
  circuit_name=args.circuit_name,
257
+ share_safe=args.share_safe,
232
258
  )
233
- print(f"Wrote preflight context JSON to {args.out_json}", file=sys.stderr)
234
- print(f"Wrote preflight context Markdown to {args.out_md}", file=sys.stderr)
259
+ json_label = "<redacted-local-path>" if args.share_safe else args.out_json
260
+ md_label = "<redacted-local-path>" if args.share_safe else args.out_md
261
+ print(f"Wrote preflight context JSON to {json_label}", file=sys.stderr)
262
+ print(f"Wrote preflight context Markdown to {md_label}", file=sys.stderr)
235
263
  return 0
236
264
 
237
265
 
@@ -248,17 +276,66 @@ def _cmd_review(argv: list[str]) -> int:
248
276
  p.add_argument("--preflight-json", default=None, help="Optional preflight context JSON path for linkage/checks")
249
277
  p.add_argument("--out-json", required=True, help="Output execution review JSON path")
250
278
  p.add_argument("--out-md", required=True, help="Output execution review Markdown path")
279
+ p.add_argument(
280
+ "--share-safe",
281
+ "--redact",
282
+ dest="share_safe",
283
+ action="store_true",
284
+ help="Write artifacts designed for safer sharing by redacting local paths and sensitive runtime details.",
285
+ )
251
286
  args = p.parse_args(argv)
252
287
 
253
- write_execution_review(
254
- counts_json=args.counts_json,
255
- counts_format=args.counts_format,
256
- preflight_json=args.preflight_json,
257
- out_json=args.out_json,
258
- out_md=args.out_md,
288
+ try:
289
+ write_execution_review(
290
+ counts_json=args.counts_json,
291
+ counts_format=args.counts_format,
292
+ preflight_json=args.preflight_json,
293
+ out_json=args.out_json,
294
+ out_md=args.out_md,
295
+ share_safe=args.share_safe,
296
+ )
297
+ except JSONDecodeError as exc:
298
+ print(
299
+ f"qcoder review: input JSON could not be parsed at line {exc.lineno}, column {exc.colno}. "
300
+ "Check that the counts/preflight file is UTF-8 JSON.",
301
+ file=sys.stderr,
302
+ )
303
+ return 2
304
+ except ValueError as exc:
305
+ print(
306
+ f"qcoder review: {exc}. For Qiskit counts, use --format qiskit_counts with a JSON object "
307
+ 'like {"00": 10, "11": 6} or {"counts": {"00": 10, "11": 6}}.',
308
+ file=sys.stderr,
309
+ )
310
+ return 2
311
+ json_label = "<redacted-local-path>" if args.share_safe else args.out_json
312
+ md_label = "<redacted-local-path>" if args.share_safe else args.out_md
313
+ print(f"Wrote execution review JSON to {json_label}", file=sys.stderr)
314
+ print(f"Wrote execution review Markdown to {md_label}", file=sys.stderr)
315
+ return 0
316
+
317
+
318
+ def _cmd_mcp(argv: list[str]) -> int:
319
+ p = argparse.ArgumentParser(
320
+ prog="qcoder mcp",
321
+ add_help=True,
322
+ description=(
323
+ "Local read-only qCoder MCP for Cursor. Uses explicit user-selected inputs only; "
324
+ "does not execute circuits, modify code, use tokens, or call live services."
325
+ ),
259
326
  )
260
- print(f"Wrote execution review JSON to {args.out_json}", file=sys.stderr)
261
- print(f"Wrote execution review Markdown to {args.out_md}", file=sys.stderr)
327
+ sub = p.add_subparsers(dest="mcp_command")
328
+ p_serve = sub.add_parser("serve", help="Serve qCoder tools over MCP stdio.")
329
+ p_serve.set_defaults(mcp_command="serve")
330
+ args = p.parse_args(argv)
331
+ if args.mcp_command is None:
332
+ p.print_help()
333
+ return 0
334
+ if args.mcp_command == "serve":
335
+ from qcoder.mcp.server import serve_stdio
336
+
337
+ return serve_stdio()
338
+ p.print_help()
262
339
  return 0
263
340
 
264
341
 
@@ -318,14 +395,20 @@ def _format_summary_value(value: str | int | float | bool) -> str:
318
395
  return str(value)
319
396
 
320
397
 
321
- def _print_raw_payload_json(payload: dict[str, object] | None) -> None:
322
- print(json.dumps(payload or {}, indent=2, sort_keys=True))
398
+ def _print_raw_payload_json(payload: dict[str, object] | None, *, share_safe: bool = False) -> None:
399
+ out = payload or {}
400
+ if share_safe:
401
+ out = make_share_safe_payload(out)
402
+ print(json.dumps(out, indent=2, sort_keys=True))
323
403
 
324
404
 
325
- def _write_json_payload(path: str, payload: dict[str, object] | None) -> None:
405
+ def _write_json_payload(path: str, payload: dict[str, object] | None, *, share_safe: bool = False) -> None:
406
+ out = payload or {}
407
+ if share_safe:
408
+ out = make_share_safe_payload(out)
326
409
  out_path = Path(path)
327
410
  out_path.parent.mkdir(parents=True, exist_ok=True)
328
- out_path.write_text(json.dumps(payload or {}, indent=2, sort_keys=True), encoding="utf-8")
411
+ out_path.write_text(json.dumps(out, indent=2, sort_keys=True), encoding="utf-8")
329
412
 
330
413
 
331
414
  def _is_scalar(value: object) -> bool:
@@ -468,6 +551,11 @@ def _render_student_evidence_markdown(payload: dict[str, object] | None) -> str:
468
551
  "# qCoder Explorer Beta Guided Evidence",
469
552
  "",
470
553
  ]
554
+ if payload and payload.get("share_safe") is True:
555
+ lines.append(render_share_safe_note().strip())
556
+ lines.append("")
557
+ lines.append(render_share_safe_provenance(payload).strip())
558
+ lines.append("")
471
559
  for line in _summarize_student_evidence_payload(payload):
472
560
  lines.append(f"- {line}")
473
561
  if payload:
@@ -490,10 +578,13 @@ def _render_student_evidence_markdown(payload: dict[str, object] | None) -> str:
490
578
  return "\n".join(lines)
491
579
 
492
580
 
493
- def _write_markdown_payload(path: str, payload: dict[str, object] | None) -> None:
581
+ def _write_markdown_payload(path: str, payload: dict[str, object] | None, *, share_safe: bool = False) -> None:
582
+ out = payload or {}
583
+ if share_safe:
584
+ out = make_share_safe_payload(out)
494
585
  out_path = Path(path)
495
586
  out_path.parent.mkdir(parents=True, exist_ok=True)
496
- out_path.write_text(_render_student_evidence_markdown(payload), encoding="utf-8")
587
+ out_path.write_text(_render_student_evidence_markdown(out), encoding="utf-8")
497
588
 
498
589
 
499
590
  def _run_student_builtin_review_check(
@@ -577,6 +668,7 @@ def _run_student_evidence_check(
577
668
  context_json_path: str | None = None,
578
669
  out_json: str | None = None,
579
670
  out_md: str | None = None,
671
+ share_safe: bool = False,
580
672
  ) -> int:
581
673
  request_payload: dict[str, object] | None = None
582
674
  if qasm_path and context_json_path:
@@ -618,11 +710,11 @@ def _run_student_evidence_check(
618
710
  return 2
619
711
 
620
712
  if json_output:
621
- _print_raw_payload_json(response.payload)
713
+ _print_raw_payload_json(response.payload, share_safe=share_safe)
622
714
  if out_json:
623
- _write_json_payload(out_json, response.payload)
715
+ _write_json_payload(out_json, response.payload, share_safe=share_safe)
624
716
  if out_md:
625
- _write_markdown_payload(out_md, response.payload)
717
+ _write_markdown_payload(out_md, response.payload, share_safe=share_safe)
626
718
  return 0 if response.status_code == 200 else 1 if response.status_code in {401, 403} else 2
627
719
 
628
720
  if response.status_code == 200:
@@ -637,11 +729,11 @@ def _run_student_evidence_check(
637
729
  for line in _summarize_student_evidence_payload(response.payload):
638
730
  print(f" {line}")
639
731
  if out_json:
640
- _write_json_payload(out_json, response.payload)
641
- print(f" wrote_json: {out_json}")
732
+ _write_json_payload(out_json, response.payload, share_safe=share_safe)
733
+ print(f" wrote_json: {'<redacted-local-path>' if share_safe else out_json}")
642
734
  if out_md:
643
- _write_markdown_payload(out_md, response.payload)
644
- print(f" wrote_md: {out_md}")
735
+ _write_markdown_payload(out_md, response.payload, share_safe=share_safe)
736
+ print(f" wrote_md: {'<redacted-local-path>' if share_safe else out_md}")
645
737
  return 0
646
738
  if response.status_code == 401:
647
739
  print(
@@ -724,6 +816,13 @@ def _cmd_explorer(argv: list[str], *, compatibility_alias: bool = False) -> int:
724
816
  )
725
817
  p_evidence.add_argument("--out-json", default=None, help="Write Explorer evidence response JSON.")
726
818
  p_evidence.add_argument("--out-md", default=None, help="Write Explorer evidence response Markdown.")
819
+ p_evidence.add_argument(
820
+ "--share-safe",
821
+ "--redact",
822
+ dest="share_safe",
823
+ action="store_true",
824
+ help="Write output artifacts with explicit share-safe metadata and local-sensitive details redacted.",
825
+ )
727
826
  p_evidence.set_defaults(explorer_command="evidence")
728
827
 
729
828
  args = p.parse_args(argv)
@@ -749,6 +848,7 @@ def _cmd_explorer(argv: list[str], *, compatibility_alias: bool = False) -> int:
749
848
  context_json_path=args.context_json,
750
849
  out_json=args.out_json,
751
850
  out_md=args.out_md,
851
+ share_safe=args.share_safe,
752
852
  )
753
853
 
754
854
  p.print_help()
@@ -1140,14 +1240,15 @@ def _cmd_pro(argv: list[str]) -> int:
1140
1240
 
1141
1241
  def _print_root_help() -> None:
1142
1242
  print(
1143
- "usage: qcoder [--version | -V] [-h] {analyze,batch,context,review,explorer,pro,student} ...\n\n"
1243
+ "usage: qcoder [--version | -V] [-h] {analyze,batch,context,review,mcp,explorer,pro,student} ...\n\n"
1144
1244
  "Quantum circuit analysis CLI.\n\n"
1145
1245
  "positional arguments:\n"
1146
- " {analyze,batch,context,review,explorer,pro,student} subcommand\n\n"
1246
+ " {analyze,batch,context,review,mcp,explorer,pro,student} subcommand\n\n"
1147
1247
  " analyze Analyze a QASM file (feature extraction + metadata + run config).\n"
1148
1248
  " batch Batch extract a directory to JSONL (requires --out).\n"
1149
1249
  " context Build deterministic preflight context artifacts.\n"
1150
1250
  " review Build deterministic execution review artifacts from counts.\n"
1251
+ " mcp Serve local read-only qCoder tools for Cursor over MCP stdio.\n"
1151
1252
  " explorer Explorer Beta status/demo/evidence checks.\n"
1152
1253
  " pro Archived Pro client-contract shell (not current public product).\n"
1153
1254
  " student Compatibility alias for Explorer Beta checks.\n\n"
@@ -1179,6 +1280,8 @@ def main(argv: list[str] | None = None) -> int:
1179
1280
  return _cmd_context(rest)
1180
1281
  if cmd == "review":
1181
1282
  return _cmd_review(rest)
1283
+ if cmd == "mcp":
1284
+ return _cmd_mcp(rest)
1182
1285
  if cmd == "explorer":
1183
1286
  return _cmd_explorer(rest)
1184
1287
  if cmd == "pro":
@@ -1187,7 +1290,7 @@ def main(argv: list[str] | None = None) -> int:
1187
1290
  return _cmd_student(rest)
1188
1291
 
1189
1292
  print(
1190
- f"qcoder: unknown subcommand {cmd!r} (expected analyze, batch, context, review, explorer, pro, or student)",
1293
+ f"qcoder: unknown subcommand {cmd!r} (expected analyze, batch, context, review, mcp, explorer, pro, or student)",
1191
1294
  file=sys.stderr,
1192
1295
  )
1193
1296
  print("Run `qcoder --help` for usage.", file=sys.stderr)
@@ -60,9 +60,15 @@ class RunConfig:
60
60
  )
61
61
 
62
62
  def to_dict(self) -> dict:
63
+ analysis_backend = "local_gpu" if self.backend == "GPU" else "local_cpu"
63
64
  return {
64
65
  "processor": self.processor,
65
66
  "backend": self.backend,
67
+ "analysis_backend": analysis_backend,
68
+ "local_analysis_backend": analysis_backend,
69
+ "backend_meaning": (
70
+ "local analysis backend; not a simulator, QPU, or hardware execution backend"
71
+ ),
66
72
  "precision": self.precision,
67
73
  "threshold": self.threshold,
68
74
  }