workspai 0.46.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +61 -12
  2. package/contracts/agent-customization-pack.v1.json +6 -1
  3. package/contracts/bootstrap-compliance.v1.json +14 -0
  4. package/contracts/cli-runtime-command-inventory.v1.snapshot.json +4 -0
  5. package/contracts/extension-cli-compatibility.v1.json +7 -1
  6. package/contracts/mirror-ops.v1.json +16 -0
  7. package/contracts/published-contract-catalog.v1.json +31 -0
  8. package/contracts/runtime-command-surface.v1.json +149 -1
  9. package/contracts/transparency-evidence.v1.json +13 -0
  10. package/contracts/workspace-contract.v1.json +78 -0
  11. package/contracts/workspace-intelligence/workspace-context.v1.json +20 -0
  12. package/contracts/workspace-intelligence/workspace-graph-token-efficiency.v1.json +72 -0
  13. package/contracts/workspace-intelligence/workspace-intelligence-run.v1.json +6 -1
  14. package/contracts/workspace-intelligence/workspace-knowledge-graph-change-overlay.v1.json +200 -0
  15. package/contracts/workspace-intelligence/workspace-knowledge-graph.v1.json +260 -0
  16. package/contracts/workspace-intelligence/workspace-knowledge-search.v1.json +60 -0
  17. package/contracts/workspace-intelligence-architecture.v1.json +6 -3
  18. package/contracts/workspace-intelligence-chain.v1.json +14 -3
  19. package/contracts/workspace-share-bundle.v1.json +16 -0
  20. package/dist/analyze-UVXPRGYZ.js +1 -0
  21. package/dist/artifact-remediation-plan-EPALZ2LC.js +3 -0
  22. package/dist/autopilot-release-5BQ6F5L2.js +1 -0
  23. package/dist/chunk-22NJ2ZMG.js +2 -0
  24. package/dist/{chunk-UQWOVV6V.js → chunk-2GHUZDYA.js} +1 -1
  25. package/dist/chunk-2TEDAKP6.js +2 -0
  26. package/dist/chunk-6SWRNA47.js +4 -0
  27. package/dist/chunk-76YOPAOT.js +1 -0
  28. package/dist/chunk-7VLCK5JW.js +1 -0
  29. package/dist/{chunk-J5PIZCAU.js → chunk-BSRVO52Y.js} +1 -1
  30. package/dist/chunk-COARSXRC.js +1 -0
  31. package/dist/chunk-CV5HKU4P.js +1 -0
  32. package/dist/chunk-CW7PGBIQ.js +13 -0
  33. package/dist/{chunk-WPEEC5BX.js → chunk-DV6GJD4K.js} +1 -1
  34. package/dist/chunk-FB7SCXAZ.js +1 -0
  35. package/dist/{chunk-4LGXSBCN.js → chunk-HSGFUKCN.js} +1 -1
  36. package/dist/{chunk-ZKAI3PJE.js → chunk-ITCAMC2E.js} +1 -1
  37. package/dist/chunk-KB44JP4M.js +2 -0
  38. package/dist/{chunk-QA5BGEQW.js → chunk-KZZ36CK5.js} +1 -1
  39. package/dist/chunk-LNRAB7UY.js +1 -0
  40. package/dist/{chunk-VFDM65IE.js → chunk-MEMHNE7Y.js} +22 -22
  41. package/dist/chunk-NOFM7MNA.js +2 -0
  42. package/dist/chunk-NRYS4CLR.js +2 -0
  43. package/dist/chunk-OA537ZQ5.js +1 -0
  44. package/dist/chunk-PBHP6JNY.js +8 -0
  45. package/dist/chunk-QDWYIRHR.js +8 -0
  46. package/dist/chunk-RWRLFSKW.js +2 -0
  47. package/dist/chunk-SK6XRKGG.js +1 -0
  48. package/dist/chunk-THIOE2PB.js +2 -0
  49. package/dist/chunk-TNQI5VCW.js +36 -0
  50. package/dist/chunk-TWNFECMN.js +2 -0
  51. package/dist/{chunk-6IIZJQLV.js → chunk-U5EZHZBX.js} +1 -1
  52. package/dist/{chunk-YUATNVOT.js → chunk-VBSQ7MF6.js} +19 -19
  53. package/dist/chunk-WDKNMTJQ.js +1 -0
  54. package/dist/chunk-YCL3I2JO.js +2 -0
  55. package/dist/chunk-ZDN7RHXJ.js +1 -0
  56. package/dist/chunk-ZM5NQ5Z2.js +1 -0
  57. package/dist/{create-WCV3L6XH.js → create-7JKJDAQV.js} +1 -1
  58. package/dist/{doctor-5BWM2EMJ.js → doctor-PGPNIS76.js} +1 -1
  59. package/dist/index.d.ts +33 -12
  60. package/dist/index.js +317 -317
  61. package/dist/pipeline-IB6ILJSV.js +5 -0
  62. package/dist/{workspace-7OXW5YTJ.js → workspace-H3QXBFGB.js} +1 -1
  63. package/dist/{workspace-agent-sync-O4IA6VOA.js → workspace-agent-sync-C7SG2Z5W.js} +1 -1
  64. package/dist/{workspace-archive-H74NBBNW.js → workspace-archive-P76EDIUG.js} +1 -1
  65. package/dist/{workspace-context-R7IPUBPG.js → workspace-context-BKQBKA4C.js} +1 -1
  66. package/dist/workspace-contract-RPQQBQXR.js +1 -0
  67. package/dist/workspace-dependency-graph-23BI2HG7.js +1 -0
  68. package/dist/workspace-explain-WVN7JH3U.js +1 -0
  69. package/dist/{workspace-explain-contract-SVFJAAEI.js → workspace-explain-contract-SEFTVF6J.js} +1 -1
  70. package/dist/{workspace-feedback-REOS36ZZ.js → workspace-feedback-WAID3IOE.js} +1 -1
  71. package/dist/{workspace-foundation-KXT4QI5O.js → workspace-foundation-5OOJEO2D.js} +1 -1
  72. package/dist/workspace-graph-token-efficiency-CFGFCJ5V.js +1 -0
  73. package/dist/{workspace-history-OGOVSKZG.js → workspace-history-C6OP3IAQ.js} +1 -1
  74. package/dist/workspace-intelligence-VKDL3H2J.js +1 -0
  75. package/dist/workspace-intelligence-runner-LVALAZY7.js +1 -0
  76. package/dist/workspace-knowledge-graph-FE2NTZKV.js +1 -0
  77. package/dist/workspace-knowledge-graph-change-overlay-XG6FC4IX.js +1 -0
  78. package/dist/workspace-knowledge-graph-query-VOSPPH4W.js +1 -0
  79. package/dist/workspace-mcp-serve-KT2I676Z.js +3 -0
  80. package/dist/workspace-model-S33CIB2R.js +1 -0
  81. package/dist/workspace-model-hash-MHXK5MEI.js +1 -0
  82. package/dist/{workspace-registry-summary-SZ46R5PD.js → workspace-registry-summary-A3YDL63D.js} +1 -1
  83. package/dist/workspace-run-M4LNJILC.js +1 -0
  84. package/dist/{workspace-verify-MFQ7IXGD.js → workspace-verify-ZGH3NXAH.js} +1 -1
  85. package/dist/workspace-watch-EVBJTMV7.js +1 -0
  86. package/docs/AI_DYNAMIC_INTEGRATION.md +73 -428
  87. package/docs/AI_EXAMPLES.md +37 -395
  88. package/docs/AI_FEATURES.md +76 -456
  89. package/docs/AI_QUICKSTART.md +49 -212
  90. package/docs/GLOSSARY.md +60 -0
  91. package/docs/OPEN_SOURCE_USER_SCENARIOS.md +68 -7
  92. package/docs/README.md +63 -41
  93. package/docs/commands-reference.md +51 -4
  94. package/docs/config-file-guide.md +6 -2
  95. package/docs/contracts/ARTIFACT_CATALOG.md +67 -37
  96. package/docs/contracts/README.md +44 -8
  97. package/docs/graph-benchmark-methodology.md +121 -0
  98. package/docs/workspace-knowledge-graph.md +295 -0
  99. package/docs/workspace-operations.md +49 -0
  100. package/package.json +2 -1
  101. package/dist/analyze-BEBEZSZK.js +0 -1
  102. package/dist/artifact-remediation-plan-FFQSESAM.js +0 -3
  103. package/dist/autopilot-release-WUR4CQIT.js +0 -1
  104. package/dist/chunk-2G7FASAO.js +0 -2
  105. package/dist/chunk-4EPHWD27.js +0 -8
  106. package/dist/chunk-CVHMUSRX.js +0 -1
  107. package/dist/chunk-DIPD72H4.js +0 -2
  108. package/dist/chunk-EFYHGCGX.js +0 -2
  109. package/dist/chunk-FWRXA435.js +0 -2
  110. package/dist/chunk-HDURFXW5.js +0 -2
  111. package/dist/chunk-HMUKBW2S.js +0 -4
  112. package/dist/chunk-K4WNYXKK.js +0 -33
  113. package/dist/chunk-LG6RFLPZ.js +0 -1
  114. package/dist/chunk-N7DV5L7C.js +0 -1
  115. package/dist/chunk-PRBVYW3T.js +0 -1
  116. package/dist/chunk-QZLIURER.js +0 -13
  117. package/dist/chunk-RIEF2DDX.js +0 -8
  118. package/dist/chunk-SXMTSV5M.js +0 -1
  119. package/dist/chunk-SXPY523X.js +0 -1
  120. package/dist/chunk-V3LRQZ36.js +0 -1
  121. package/dist/chunk-WYFPXTTS.js +0 -2
  122. package/dist/pipeline-ORIWVVYM.js +0 -5
  123. package/dist/workspace-contract-HKCMOMFE.js +0 -1
  124. package/dist/workspace-explain-GOPQYTPQ.js +0 -1
  125. package/dist/workspace-intelligence-7IESQSXY.js +0 -1
  126. package/dist/workspace-intelligence-runner-6GJ5M4HB.js +0 -1
  127. package/dist/workspace-mcp-serve-FRVWBO36.js +0 -3
  128. package/dist/workspace-model-PPYX7B4S.js +0 -1
  129. package/dist/workspace-run-V3KKHTVF.js +0 -1
  130. package/dist/workspace-watch-SOPZHRWA.js +0 -1
@@ -0,0 +1,121 @@
1
+ # Graph Retrieval Benchmark Methodology
2
+
3
+ This document defines what Workspai measures when it reports graph retrieval
4
+ payload reduction, how to reproduce a result, and what the result does—and does
5
+ not—prove.
6
+
7
+ ## The question being measured
8
+
9
+ For one query, how much smaller is the bounded, proof-carrying graph response
10
+ than the readable source corpus that supplied the graph's proofs?
11
+
12
+ This is useful because AI agents do not need every indexed file for every
13
+ question. It is deliberately narrower than “How many tokens will this model
14
+ bill?” or “Will the model produce an equally good answer?”
15
+
16
+ ## Run it
17
+
18
+ From a Workspai workspace:
19
+
20
+ ```bash
21
+ npx workspai workspace model --write --json
22
+ npx workspai workspace graph benchmark "authentication endpoint" --limit 12 --json
23
+ ```
24
+
25
+ The result conforms to
26
+ [`workspace-graph-token-efficiency.v1.json`](../contracts/workspace-intelligence/workspace-graph-token-efficiency.v1.json).
27
+ It records:
28
+
29
+ - the query and result limit;
30
+ - the graph schema, entity/relation/proof counts, source artifact, and source
31
+ model SHA-256;
32
+ - the number and size of readable, deduplicated proof-source artifacts;
33
+ - the bounded retrieval size and match count;
34
+ - unreadable artifacts rather than silently excluding them;
35
+ - the estimate formula, reduction ratio, percentage, and claim boundary.
36
+
37
+ ## Baseline and formula
38
+
39
+ The current methodology is `indexed-corpus-vs-bounded-retrieval.v1`.
40
+
41
+ ```text
42
+ corpus characters = sum(unique readable proof-source files)
43
+ retrieval characters = compact JSON length of bounded search response
44
+ estimated tokens = ceil(characters / 4)
45
+ reduction ratio = corpus estimated tokens / retrieval estimated tokens
46
+ reduction percent = (corpus - retrieval) / corpus × 100
47
+ ```
48
+
49
+ `characters / 4` is intentionally labelled as an estimate. It is portable and
50
+ reproducible without downloading a tokenizer, but it is not exact for every
51
+ language, model, or tokenizer.
52
+
53
+ ## Current fixture observation
54
+
55
+ The 16-project development workspace produced the following result on
56
+ 2026-07-21 for `api endpoint --limit 8`:
57
+
58
+ | Measure | Observed value |
59
+ | ------------------------------- | -------------: |
60
+ | Graph entities | 1,738 |
61
+ | Graph relations | 2,244 |
62
+ | Graph proofs | 2,106 |
63
+ | Readable proof-source artifacts | 392 |
64
+ | Corpus estimated tokens | 134,105 |
65
+ | Retrieval estimated tokens | 2,812 |
66
+ | Returned entities | 8 |
67
+ | Retrieval ratio | 47.69× |
68
+ | Payload reduction | 97.9% |
69
+
70
+ Source-model SHA-256:
71
+ `2b8abd415420cc421707c726e6f6c96641554594e84440bf2e539f04ba5836e8`.
72
+
73
+ This row demonstrates that measurement is possible. It is not a representative
74
+ cross-project benchmark and must not be marketed as a universal Workspai result.
75
+
76
+ ## What can be claimed today
77
+
78
+ Safe wording:
79
+
80
+ > Workspai can return bounded, proof-carrying workspace context instead of the
81
+ > complete indexed corpus. On the current 16-project development fixture, one
82
+ > `api endpoint` query reduced the estimated retrieval payload by 97.9%; results
83
+ > vary by workspace and query.
84
+
85
+ Unsafe wording:
86
+
87
+ - “Workspai always reduces model tokens by 97.9%.”
88
+ - “Agents are 47.69× cheaper with no quality loss.”
89
+ - “Workspai beats another product” without a shared corpus and evaluation
90
+ harness.
91
+
92
+ ## Gate for a public headline benchmark
93
+
94
+ Before publishing a general token-efficiency number, the benchmark suite must:
95
+
96
+ 1. pin public repositories and exact commit SHAs;
97
+ 2. publish fixed question sets and graph configuration;
98
+ 3. compare at least three baselines:
99
+ - entire readable corpus;
100
+ - a realistic grep/top-file retrieval strategy;
101
+ - bounded Workspai graph retrieval;
102
+ 4. count with at least one real, named tokenizer in addition to the portable
103
+ character estimate;
104
+ 5. measure answer relevance or task completion so smaller context is not treated
105
+ as automatically better context;
106
+ 6. repeat runs and publish variance, failures, unreadable files, hardware, and
107
+ software versions;
108
+ 7. publish raw machine-readable results and a one-command reproduction path;
109
+ 8. report median and range—not only the best repository.
110
+
111
+ ## Performance is a separate benchmark
112
+
113
+ Payload size and graph speed answer different questions. Build time, incremental
114
+ update time, peak memory, artifact size, and p50/p95 query latency must be
115
+ measured separately. Do not infer runtime performance from the token-efficiency
116
+ report.
117
+
118
+ For normal interactive use, prefer `workspace graph search` or the MCP
119
+ `searchWorkspaceGraph` tool. Use the complete graph artifact for interchange,
120
+ offline analysis, audits, and consumers that explicitly require the entire
121
+ workspace representation.
@@ -0,0 +1,295 @@
1
+ # Workspace Knowledge Graph
2
+
3
+ Most code graphs answer questions about one repository. Workspai connects code,
4
+ APIs, infrastructure, delivery, documentation, ownership, tests, and runtime
5
+ configuration across a whole workspace—and records why every relationship is
6
+ believed.
7
+
8
+ The graph is local-first and deterministic. Building it does not require an
9
+ LLM, a hosted service, embeddings, or a graph database.
10
+
11
+ ## Why this is useful
12
+
13
+ A repository graph can tell you that function A calls function B. A workspace
14
+ question is usually wider:
15
+
16
+ > If we change this endpoint, which project consumes it, which deployment ships
17
+ > it, which tests cover it, which document describes it, and which release gate
18
+ > can stop it?
19
+
20
+ Workspai keeps those domains in one proof-carrying representation. The graph is
21
+ not the final product screen; it is the shared knowledge layer behind impact,
22
+ verification, context, MCP, IDE, CI, and agent workflows.
23
+
24
+ | If you are… | The graph helps you… |
25
+ | -------------------- | ------------------------------------------------------------------------------------------ |
26
+ | A developer | find the implementation, nearby dependencies, tests, and evidence without repo-wide search |
27
+ | A tech lead | inspect cross-project boundaries, owners, contracts, and change paths |
28
+ | An AI coding agent | retrieve question-sized context and verify every returned claim |
29
+ | A CI/release system | consume versioned JSON, source hashes, quality diagnostics, and deterministic exits |
30
+ | An IDE or MCP client | offer the same system understanding without rebuilding a private index |
31
+
32
+ ## What makes it a workspace graph
33
+
34
+ Workspai does not stop at files, imports, functions, and classes. Repository
35
+ structure is one provider domain alongside APIs, containers, infrastructure,
36
+ pipelines, documents, decisions, ownership, tests, environments, and authored
37
+ workspace contracts.
38
+
39
+ That distinction matters when the answer crosses repositories. The canonical
40
+ Workspace Model remains the source of truth; the Knowledge Graph is its rich,
41
+ queryable, evidence-backed representation. AI is a consumer, never a requirement
42
+ for building the graph.
43
+
44
+ ## Try it in two minutes
45
+
46
+ Run from a Workspai workspace:
47
+
48
+ ```bash
49
+ npx workspai workspace model --write --json
50
+ npx workspai workspace graph search "authentication endpoint" --limit 12 --json
51
+ ```
52
+
53
+ Useful follow-up questions:
54
+
55
+ ```bash
56
+ # What APIs and endpoints exist?
57
+ npx workspai workspace graph entities endpoint --json
58
+
59
+ # Why does Workspai believe this entity exists?
60
+ npx workspai workspace graph evidence "GET /users" --json
61
+
62
+ # How are two services, files, or APIs connected?
63
+ npx workspai workspace graph path frontend-api "GET /users" --json
64
+
65
+ # What changed since a saved graph revision?
66
+ npx workspai workspace graph overlay --from previous-graph.json --json
67
+ ```
68
+
69
+ For agents and MCP clients, prefer bounded search over loading the complete
70
+ graph:
71
+
72
+ ```bash
73
+ npx workspai workspace graph search "billing database" --limit 12 --json
74
+ ```
75
+
76
+ A simplified response looks like this:
77
+
78
+ ```json
79
+ {
80
+ "schemaVersion": "workspace-knowledge-search.v1",
81
+ "query": "billing database",
82
+ "totalMatches": 23,
83
+ "truncated": true,
84
+ "entities": [{ "kind": "database", "label": "billing-db", "proofIds": ["proof:..."] }],
85
+ "relations": [{ "kind": "reads-from", "from": "service:billing", "to": "database:billing-db" }],
86
+ "proofs": [{ "provider": "compose", "artifact": "infra/compose.yml", "trust": "authoritative" }]
87
+ }
88
+ ```
89
+
90
+ The response is intentionally bounded. `totalMatches` tells the consumer more
91
+ results exist, `truncated` prevents silent omission, and every returned claim can
92
+ be traced through `proofIds`.
93
+
94
+ ## Pick the command by question
95
+
96
+ | You want to know… | Use |
97
+ | ---------------------------------------------------- | ------------------------------------------------------ |
98
+ | What is relevant to a natural-language question? | `workspace graph search <query> --limit <n> --json` |
99
+ | Which entities of one type exist? | `workspace graph entities <kind> --json` |
100
+ | Why does Workspai believe an item exists? | `workspace graph evidence <entity-or-relation> --json` |
101
+ | How are two things connected? | `workspace graph path <from> <to> --json` |
102
+ | What changed between graph revisions? | `workspace graph overlay --from <graph.json> --json` |
103
+ | What is the full portable graph? | `workspace graph emit --json` |
104
+ | How much retrieval payload did one query avoid? | `workspace graph benchmark <query> --limit <n> --json` |
105
+ | How should an MCP-compatible agent retrieve context? | `workspace mcp serve` → `searchWorkspaceGraph` |
106
+
107
+ ## What it models
108
+
109
+ The current graph can represent:
110
+
111
+ - workspaces, projects, services, packages, modules, files, and symbols;
112
+ - APIs, endpoints, schemas, events, queues, and databases;
113
+ - containers, deployments, environments, pipelines, and infrastructure;
114
+ - documentation, architecture decisions, tests, and owners.
115
+
116
+ Relations include `contains`, `imports`, `depends-on`, `calls`, `exposes`,
117
+ `implements`, `reads-from`, `writes-to`, `publishes`, `consumes`, `deploys`,
118
+ `documents`, `decided-by`, `tests`, and `owns`.
119
+
120
+ Every entity and relation carries portable proof references. A proof records its
121
+ provider, source artifact, optional pointer/line, content hash, freshness,
122
+ derivation, trust, and confidence. Secret values and machine-local absolute
123
+ paths are excluded from the portable graph contract.
124
+
125
+ ## Model first, graph second
126
+
127
+ The canonical direction is one-way:
128
+
129
+ ```text
130
+ Workspace sources
131
+
132
+ Canonical Workspace Model + project topology
133
+
134
+ Evidence-backed Workspace Knowledge Graph
135
+
136
+ CLI queries · Context · MCP · Agents · IDEs · CI
137
+ ```
138
+
139
+ The graph does not rewrite or replace the canonical Workspace Model. It is a
140
+ derived representation bound to an exact model revision by SHA-256. Context and
141
+ MCP consumers reject a graph whose source hash no longer matches the model.
142
+
143
+ The model also contains a smaller project dependency graph. That projection is
144
+ used for impact, blast radius, verify, explain, watch, and affected fleet runs.
145
+ The richer Knowledge Graph is used for proof-backed retrieval and cross-domain
146
+ understanding.
147
+
148
+ ## Sources and providers
149
+
150
+ The current CLI uses bounded providers for:
151
+
152
+ - workspace/project foundations and service contracts;
153
+ - language-neutral source structure and package manifests;
154
+ - OpenAPI, GraphQL, Protobuf, and AsyncAPI interfaces;
155
+ - Docker/Compose, Kubernetes, Terraform, and CI workflows;
156
+ - README/docs, ADRs, tests, and CODEOWNERS.
157
+
158
+ Providers emit facts and proofs. The graph engine owns stable identity,
159
+ deduplication, typed relations, reconciliation, quality metrics, diagnostics,
160
+ and deterministic ordering. Regex-backed source observations are explicitly
161
+ marked as observed/medium-confidence; authored contracts remain authoritative.
162
+
163
+ ## Outputs and consumers
164
+
165
+ `workspace model --write` publishes these two artifacts atomically:
166
+
167
+ ```text
168
+ .workspai/reports/workspace-model.json
169
+ .workspai/reports/workspace-knowledge-graph.json
170
+ ```
171
+
172
+ The Knowledge Graph is consumed by:
173
+
174
+ - `workspace graph search|entities|evidence|path|overlay`;
175
+ - `workspace context`, which validates the model hash and publishes graph
176
+ availability and query commands;
177
+ - `workspace agent-sync`, which places it in the evidence index and generated
178
+ agent/MCP instructions;
179
+ - `workspace mcp serve`, through `getWorkspaceKnowledgeGraph`,
180
+ `searchWorkspaceGraph`, `queryWorkspaceEntities`,
181
+ `getWorkspaceGraphEvidence`, and `findWorkspaceGraphPath`;
182
+ - `workspace contract graph`, which exposes the contract projection, project
183
+ topology, rich graph, and quality summary in one response.
184
+
185
+ The complete graph is an interchange artifact, not a prompt. Agents should
186
+ start with `INDEX.json`, use bounded search, then retrieve evidence or a path
187
+ for the selected result.
188
+
189
+ ## AI tool output locations
190
+
191
+ `workspace agent-sync --write --preset enterprise` generates native surfaces
192
+ without changing project source:
193
+
194
+ | Consumer | Canonical output |
195
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
196
+ | Cross-tool/Codex | `AGENTS.md`, `.workspai/reports/INDEX.json` |
197
+ | Claude Code | `CLAUDE.md`, `.claude/rules/workspai-evidence.md` |
198
+ | GitHub Copilot/VS Code | `.github/copilot-instructions.md`, `.github/instructions/workspai-*.instructions.md`, `.github/agents/workspai-*.agent.md`, `.github/prompts/workspai-*.prompt.md` |
199
+ | Cursor | `.cursor/rules/workspai-grounding.mdc` |
200
+ | MCP clients | `.workspai/reports/workspai-mcp-design.json`, `workspace mcp serve` |
201
+
202
+ Files named `rapidkit-*` are compatibility aliases for older consumers. They
203
+ must stay narrowly scoped and must not duplicate an always-applied canonical
204
+ rule.
205
+
206
+ ### Avoiding duplicate AI instructions
207
+
208
+ Canonical Workspai rules are the only always-applied surfaces. Legacy RapidKit
209
+ aliases point to the canonical files and apply only to `.rapidkit/**`. This
210
+ prevents two equivalent instruction files from being injected into one prompt.
211
+
212
+ The recommended read order is:
213
+
214
+ 1. `AGENTS.md` for stable workspace policy and navigation;
215
+ 2. `.workspai/reports/INDEX.json` for artifact discovery and freshness;
216
+ 3. `workspace graph search` or `searchWorkspaceGraph` for question-sized facts;
217
+ 4. `workspace graph evidence|path` when a claim needs proof;
218
+ 5. the complete model or graph only for export, audit, or whole-system analysis.
219
+
220
+ ## Performance and scale
221
+
222
+ Graph construction inventories each project once per build and caps the number
223
+ of scanned files. Providers reuse the same in-memory inventory and content
224
+ hashes. Query indexes are cached per immutable graph object; replacing the graph
225
+ is the invalidation boundary. `workspace model --cache` and `--incremental`
226
+ avoid unnecessary model/project work when inputs are unchanged.
227
+
228
+ Use full graph export for interchange or offline analysis. Use bounded search
229
+ for interactive agents. The latter keeps response size proportional to the
230
+ question instead of workspace size.
231
+
232
+ ## Measuring retrieval payload reduction
233
+
234
+ Workspai does not publish an unqualified “N× fewer tokens” claim. Such a claim
235
+ depends on the workspace, query, tokenizer, model, answer-quality target, and
236
+ baseline.
237
+
238
+ Measure the current workspace instead:
239
+
240
+ ```bash
241
+ npx workspai workspace graph benchmark "authentication endpoint" --limit 12 --json
242
+ ```
243
+
244
+ The report compares the readable, proof-indexed source corpus with the bounded
245
+ search payload using a clearly labelled `characters / 4` token estimate. It
246
+ reports corpus size, retrieval size, estimated ratio, percentage reduction,
247
+ unreadable artifacts, query, limit, graph counts, and the exact source-model
248
+ SHA-256 needed to reproduce the run.
249
+
250
+ This proves **retrieval payload reduction**, not equivalent answer quality,
251
+ model-specific billing savings, or universal token savings. A publishable
252
+ cross-project claim additionally requires pinned source revisions, fixed
253
+ queries, a real tokenizer, repeated runs, and answer-quality evaluation.
254
+
255
+ In the current 16-project development fixture, the query `api endpoint` with
256
+ `--limit 8` returned 8 entities and 9 proofs. The compact retrieval was 2,812
257
+ estimated tokens versus 134,105 estimated tokens in 392 readable proof-source
258
+ artifacts: an observed 47.69× / 97.9% payload reduction. This is a transparent
259
+ fixture result, not a headline claim for every workspace.
260
+
261
+ See [Graph Benchmark Methodology](./graph-benchmark-methodology.md) for formulas,
262
+ reproduction rules, realistic baselines, and the gate required before publishing
263
+ a general performance claim.
264
+
265
+ ## Current boundaries
266
+
267
+ - The CLI graph is intentionally file-backed; a graph database is not required.
268
+ - Text search is deterministic lexical retrieval, not embedding similarity.
269
+ - Compiler/LSP-grade symbol resolution belongs in deeper language providers.
270
+ - Missing project edges mean “relationship not proven,” not “projects are
271
+ independent.” Author service contracts or provide API/package/runtime
272
+ evidence to close that gap.
273
+ - The standalone `@workspai/graph` package remains unpublished while its public
274
+ contracts and conformance gates are developed.
275
+
276
+ ## When the graph looks incomplete
277
+
278
+ Workspai does not invent relationships. If projects appear as disconnected
279
+ nodes, check the following in order:
280
+
281
+ 1. run `workspace sync --json` and regenerate the model;
282
+ 2. declare `dependsOn`, APIs, published events, and consumed events in the
283
+ workspace/project contracts;
284
+ 3. confirm package manifests, OpenAPI/AsyncAPI/GraphQL/Protobuf documents,
285
+ Compose/Kubernetes/Terraform files, and CI definitions are inside registered
286
+ project paths;
287
+ 4. inspect `graph.quality`, `providers`, and `diagnostics` before treating a
288
+ missing edge as proof of independence.
289
+
290
+ An absent edge means “not proven by current evidence,” not “no dependency
291
+ exists.”
292
+
293
+ For schemas and machine contracts, see the
294
+ [Artifact Catalog](./contracts/ARTIFACT_CATALOG.md). For the full command
295
+ surface, see [Commands Reference](./commands-reference.md).
@@ -94,6 +94,55 @@ npx workspai workspace contract graph
94
94
 
95
95
  Contract file: `.workspai/workspace.contract.json`. Verification checks schema, duplicate slugs, port collisions, and unknown dependencies.
96
96
 
97
+ `workspace contract graph --json` preserves its original `nodes`, `edges`, and
98
+ summary fields for existing consumers, and now adds an evidence-backed
99
+ `dependencyGraph` using the public `workspace-dependency-graph.v1` contract. It
100
+ discovers package/workspace dependencies and supported cross-project imports,
101
+ records relationship provenance and confidence, and reports graph coverage,
102
+ orphans, hotspots, and cycles. Project nodes also expose safe package metadata,
103
+ public environment-template keys, command capabilities, key manifests,
104
+ entrypoints, API specifications, infrastructure, documentation, and an
105
+ operational verification profile. Environment values are never emitted.
106
+
107
+ The same response also exposes `knowledgeGraph` under the public
108
+ `workspace-knowledge-graph.v1` contract. It is a provider-neutral,
109
+ proof-carrying view spanning source structure, packages, service and API
110
+ contracts, infrastructure, delivery pipelines, docs/ADRs, ownership, tests,
111
+ runtime resources, and safe configuration keys. Use the dedicated query and
112
+ change-overlay surfaces when the full document is larger than a human needs:
113
+
114
+ ```bash
115
+ npx workspai workspace graph entities endpoint --json
116
+ npx workspai workspace graph search "authentication endpoint" --limit 12 --json
117
+ npx workspai workspace graph benchmark "authentication endpoint" --limit 12 --json
118
+ npx workspai workspace graph evidence "GET /users" --json
119
+ npx workspai workspace graph path frontend-api "GET /users" --json
120
+ npx workspai workspace graph emit --json > .workspai/reports/knowledge-baseline.json
121
+ npx workspai workspace graph overlay --from .workspai/reports/knowledge-baseline.json --json
122
+ ```
123
+
124
+ The Model step persists the same projection to
125
+ `.workspai/reports/workspace-knowledge-graph.json`. It is registered in the
126
+ artifact contract registry and agent report index, required by the unified
127
+ runner's Model stage, referenced from `workspace-context-agent.json`, and
128
+ queryable through the read-mostly MCP server. This keeps CLI, CI, IDE and agent
129
+ consumers on one contract revision without injecting the full graph into every
130
+ agent prompt.
131
+
132
+ The overlay follows
133
+ `workspace-knowledge-graph-change-overlay.v1`: graph revisions are identified
134
+ by content-derived fingerprints (timestamps do not create false changes), and
135
+ changed artifacts are portable proof paths rather than machine-local absolute
136
+ paths. Proof additions, removals, and content-hash changes are first-class
137
+ overlay changes, so a source edit is visible even when the entity and relation
138
+ shape remains stable. The builder performs one bounded inventory pass per project and reuses
139
+ in-memory content hashes; query indexes live only for the immutable graph
140
+ instance, so replacing the graph is the invalidation boundary.
141
+
142
+ Direction is explicit: legacy `edges` remain producer-to-consumer for backward
143
+ compatibility; `dependencyGraph.edges` use consumer-to-dependency semantics so
144
+ impact and blast-radius consumers share one canonical interpretation.
145
+
97
146
  Workspai keeps the contract alive during `create project` and `workspace sync` without overwriting manual API/event/owner declarations.
98
147
 
99
148
  On a freshly cloned or moved workspace, `workspace sync` also repairs the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workspai",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "type": "module",
5
5
  "description": "Open-source workspace intelligence CLI for software systems: create, adopt, govern, verify, and align polyglot workspaces for humans, CI, IDEs, and AI agents.",
6
6
  "keywords": [
@@ -158,6 +158,7 @@
158
158
  "openai": "^6.22.0",
159
159
  "ora": "^8.0.1",
160
160
  "validate-npm-package-name": "^5.0.1",
161
+ "yaml": "^2.9.0",
161
162
  "yauzl": "^3.4.0",
162
163
  "yazl": "^3.3.1"
163
164
  },
@@ -1 +0,0 @@
1
- export{b as printAnalyzeReport,a as runAnalyze}from'./chunk-RIEF2DDX.js';
@@ -1,3 +0,0 @@
1
- import {a}from'./chunk-2E6WGUN6.js';import {c,g,d}from'./chunk-HDURFXW5.js';import y from'path';import {existsSync}from'fs';import x from'fs-extra';var I=[{artifactKind:"bootstrap-compliance",cardId:"bootstrap",fileNames:["bootstrap-compliance.latest.json"]},{artifactKind:"doctor-workspace",cardId:"doctor",fileNames:["doctor-remediation-plan-last-run.json"]},{artifactKind:"analyze",cardId:"analyze",fileNames:["analyze-last-run.json"]},{artifactKind:"readiness",cardId:"readiness",fileNames:["release-readiness-last-run.json"]},{artifactKind:"pipeline",cardId:"pipeline",fileNames:["pipeline-last-run.json"]},{artifactKind:"workspace-run",cardId:"workspaceRun",fileNames:["workspace-run-last.json"]},{artifactKind:"workspace-verify",cardId:"workspaceVerify",fileNames:["workspace-verify-last-run.json"]}];function m(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:void 0}function f(e){return Array.isArray(e)?e.filter(r=>typeof r=="string"&&r.trim().length>0):[]}function u(e){return [...new Set(e.map(r=>r.trim()).filter(Boolean))]}function K(e,r,t){return t?r:y.relative(e,r)}function C(e){let r=e?.trim()||"npx workspai bootstrap";if(!/(?:^|\s)--ci(?:\s|$)/.test(r)&&/(?:^|\s)--json(?:\s|$)/.test(r))return r.replace(/(?:^|\s)--json(?:\s|$)/,i=>{let n=i.startsWith(" ")?" ":"",c=i.endsWith(" ")?" ":"";return `${n}--ci --json${c}`}).trim();let t=/(?:^|\s)--ci(?:\s|$)/.test(r)?r:`${r} --ci`;return /(?:^|\s)--json(?:\s|$)/.test(t)?t:`${t} --json`}function M(e){return `${JSON.stringify({schemaVersion:"rapidkit.compatibility-matrix.v1",generatedAt:e,source:"workspai workspace remediation-plan",runtimes:{},notes:["Minimal enterprise baseline. Add runtime and toolchain entries as governance matures."]},null,2)}
2
- `}function N(e){return `${JSON.stringify({schema_version:"1.0",enabled:false,strategy:"on-demand",artifacts:[],created_at:e,note:"Minimal enterprise baseline. Set enabled: true and add artifact entries to activate mirroring."},null,2)}
3
- `}function P(e){let r=e.payload,t=f(r.blockers),i=f(r.blockingReasons),n=f(r.failures),c=f(r.reasons),d=f(r.policyViolations),a=m(r.summary),l=f(a?.blockers);if(e.artifactKind==="analyze"){let o=(Array.isArray(r.findings)?r.findings:[]).map(s=>m(s)).filter(s=>!!s).map(s=>String(s.message??s.title??s.id??"").trim()).filter(Boolean);return u([...t,...o,...i])}if(e.artifactKind==="workspace-run"){let g=m(r.stages),o=Object.entries(g??{}).flatMap(([s,b])=>{let A=m(b);return (Array.isArray(A?.projects)?A.projects:[]).map(p=>m(p)).filter(p=>!!p).filter(p=>p.status==="failed"||p.ok===false).map(p=>`${s}: ${String(p.project??p.name??"project")} failed`)});return u([...t,...o,...i])}return u([...t,...i,...n,...c,...d,...l])}function k(e){return {id:e.id,artifactKind:e.artifactKind,cardId:e.cardId,title:e.title,order:e.order,phase:e.phase,scope:"workspace",status:e.status??"ready",mode:e.mode,risk:e.risk,requiresApproval:true,blocker:e.blocker,summary:e.summary,...e.command?{command:e.command}:{},verifyCommand:e.verifyCommand,cwd:"workspace",files:e.files??[],...e.operation?{operation:e.operation}:{},rollback:{available:!!e.operation,strategy:e.operation?"idempotent":"none"},notes:e.notes??[]}}function S(e){let r=[],t=C("npx workspai bootstrap --json");for(let i of e.blockers)i.includes("profile.enterprise.ci")&&r.push(k({id:"bootstrap.enterprise-ci",artifactKind:"bootstrap-compliance",cardId:"bootstrap",title:"Run bootstrap in deterministic CI mode",order:10,phase:"bootstrap-preflight",blocker:i,summary:"Enterprise bootstrap compliance requires --ci for deterministic execution.",mode:"run-command",risk:"safe",command:t,verifyCommand:t})),i.includes("profile.enterprise.compatibility-matrix")&&r.push(k({id:"bootstrap.compatibility-matrix",artifactKind:"bootstrap-compliance",cardId:"bootstrap",title:"Create enterprise compatibility matrix baseline",order:20,phase:"bootstrap-config",blocker:i,summary:"Create the missing compatibility matrix baseline without overwriting user data.",mode:"edit-file",risk:"safe",verifyCommand:t,files:[".workspai/compatibility-matrix.json"],operation:{type:"file-create",path:".workspai/compatibility-matrix.json",content:M(e.generatedAt),overwrite:false}})),i.includes("profile.enterprise.mirror-config")&&r.push(k({id:"bootstrap.mirror-config",artifactKind:"bootstrap-compliance",cardId:"bootstrap",title:"Create enterprise mirror config baseline",order:30,phase:"bootstrap-config",blocker:i,summary:"Create the missing mirror configuration baseline without enabling mirroring.",mode:"edit-file",risk:"safe",verifyCommand:t,files:[".workspai/mirror-config.json"],operation:{type:"file-create",path:".workspai/mirror-config.json",content:N(e.generatedAt),overwrite:false}}));return r}function $(e){let t={analyze:{title:"Refresh analyze evidence",phase:"analysis",command:"npx workspai analyze --strict --json",verify:"npx workspai analyze --strict --json"},readiness:{title:"Refresh release readiness",phase:"release-readiness",command:e.ciMode?"npx workspai readiness --strict --json":"npx workspai readiness --json",verify:e.ciMode?"npx workspai readiness --strict --json":"npx workspai readiness --json"},pipeline:{title:"Rerun governance pipeline",phase:"governance-pipeline",command:"npx workspai pipeline --json --strict",verify:"npx workspai pipeline --json --strict"},"workspace-run":{title:"Rerun failed workspace stage",phase:"fleet-run",command:e.ciMode?"npx workspai workspace run test --strict --json":"npx workspai workspace run test --json",verify:e.ciMode?"npx workspai workspace run test --strict --json":"npx workspai workspace run test --json"},"workspace-verify":{title:"Refresh workspace verify gate",phase:"verification",command:e.ciMode?"npx workspai workspace verify --strict --json":"npx workspai workspace verify --json",verify:e.ciMode?"npx workspai workspace verify --strict --json":"npx workspai workspace verify --json"},"doctor-workspace":{title:"Use Doctor remediation plan",phase:"doctor-remediation",command:"npx workspai doctor workspace --plan --json",verify:"npx workspai doctor workspace --json"}}[e.report.artifactKind]??{title:"Refresh artifact evidence",phase:"evidence-refresh",command:"npx workspai pipeline --json --strict",verify:"npx workspai pipeline --json --strict"};return k({id:`${e.report.cardId}.refresh.${e.order}`,artifactKind:e.report.artifactKind,cardId:e.report.cardId,title:t.title,order:e.order,phase:t.phase,blocker:e.blocker,summary:"No deterministic file operation is available for this artifact yet; refresh the evidence and continue with the card-specific plan.",mode:e.report.artifactKind==="doctor-workspace"?"verify-before-fix":"run-command",risk:"guarded",status:e.report.artifactKind==="doctor-workspace"?"review-required":"ready",command:t.command,verifyCommand:t.verify,notes:e.report.artifactKind==="doctor-workspace"?["Read doctor-remediation-plan-last-run.json for ordered file-level steps."]:[]})}async function O(e){let r=[c(e,".workspai/reports"),d(e,".workspai/reports")],t=[],i=new Set;for(let n of I)for(let c of n.fileNames){let d=(await Promise.all(r.map(a=>y.join(a,c)))).find(a=>existsSync(a));if(!(!d||i.has(`${n.artifactKind}:${c}`))){i.add(`${n.artifactKind}:${c}`);try{let a=await x.readJSON(d),l=m(a);if(!l)continue;t.push({artifactKind:n.artifactKind,cardId:n.cardId,fileName:c,absolutePath:d,payload:l});}catch{t.push({artifactKind:n.artifactKind,cardId:n.cardId,fileName:c,absolutePath:d,payload:{blockers:[`${n.artifactKind}: report exists but could not be parsed.`]}});}}}return t}async function F(e){let r=y.resolve(e.workspacePath),t=e.includeAbsolutePaths===true,i=e.ciMode===true,n=new Date().toISOString(),c$1=c(r,".workspai/reports"),d=await O(r),a$1=[],l=1;for(let o of d){let s=P(o);if(s.length!==0){if(o.artifactKind==="bootstrap-compliance"){a$1.push(...S({blockers:s,generatedAt:n})),l=a$1.length+1;continue}for(let b of s.slice(0,8))a$1.push($({report:o,blocker:b,order:l,ciMode:i})),l+=1;}}let g={safe:a$1.filter(o=>o.risk==="safe").length,guarded:a$1.filter(o=>o.risk==="guarded").length,invasive:a$1.filter(o=>o.risk==="invasive").length};return {schemaVersion:a,generatedAt:n,workspace:{name:y.basename(r),...t?{path:r}:{}},source:{command:"workspace remediation-plan",reportsDir:K(r,c$1,t),includeAbsolutePaths:t,ciMode:i},summary:{artifactsScanned:d.length,cardsCovered:u(a$1.map(o=>o.cardId)).length,totalActions:a$1.length,executableActions:a$1.filter(o=>o.status==="ready").length,risk:g},actions:a$1}}async function W(e,r){return g(r,".workspai/reports/artifact-remediation-plan-last-run.json",e)}export{F as buildArtifactRemediationPlan,W as writeArtifactRemediationPlan};
@@ -1 +0,0 @@
1
- export{b as AUTOPILOT_RELEASE_ALIAS_FILENAME,a as AUTOPILOT_RELEASE_LAST_RUN_FILENAME,c as runAutopilotRelease}from'./chunk-4EPHWD27.js';
@@ -1,2 +0,0 @@
1
- import {b as b$1}from'./chunk-NHN4QXPP.js';import {i}from'./chunk-MER6ZBN2.js';import {c}from'./chunk-PRBVYW3T.js';import {a}from'./chunk-Y45WZR5N.js';import {f}from'./chunk-7YHK5NM3.js';import {b}from'./chunk-RO73VVHW.js';import {h,g as g$1}from'./chunk-5XATWNME.js';import {promises,constants}from'fs';import c$1 from'path';import T,{homedir}from'os';import {pathToFileURL}from'url';import g from'process';import E from'crypto';import s from'fs/promises';function q(t){let e=t.label.indexOf(" \u2014 ");return e>=0?t.label.slice(e+3).trim():t.label.trim()}function B(){let t=c().map(e=>({value:e.id,label:q(e),hint:e.description,name:e.label})),r=i().map(e=>({value:e.kitId,label:e.displayName,hint:e.commandDisplay("my-app"),name:`${e.displayName} \u2014 ${e.framework}`}));return [...t,...r]}var Q=".workspairc.json",z=".rapidkitrc.json",Z=["workspai.config.js","workspai.config.mjs","workspai.config.cjs","rapidkit.config.js","rapidkit.config.mjs","rapidkit.config.cjs"],ee=["workspai.config.json","rapidkit.config.json"];function I(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}function L(t,r){let e=r instanceof Error?r.message:String(r),i=t.endsWith(".js")&&/module is not defined in ES module scope/i.test(e)?' CommonJS syntax in a .js config is not valid when the project package uses "type": "module"; rename the file to .cjs or use export default.':"";return `Failed to load Workspai config at ${t}.${i} Cause: ${e}`}async function _e(){for(let t of [c$1.join(T.homedir(),Q),c$1.join(T.homedir(),z)])try{let r=await promises.readFile(t,"utf-8"),e=JSON.parse(r);return f.debug(`Loaded config from ${t}`),e}catch{}return f.debug("No user config found, using defaults"),{}}async function Ke(t=process.cwd(),r={}){let e=t,i=c$1.parse(e).root;for(;e!==i;){for(let o of ee){let n=c$1.join(e,o);try{let a=await promises.readFile(n,"utf-8"),l=JSON.parse(a);if(!l||typeof l!="object"||Array.isArray(l))throw new Error("configuration root must be a JSON object");return f.debug(`Loaded data-only Workspai config from ${o}`),l}catch(a){if(I(a))continue;throw new Error(L(n,a))}}for(let o of Z){let n=c$1.join(e,o);try{await promises.access(n);}catch(a){if(I(a))continue;throw new Error(L(n,a))}if(f.debug(`Found config file: ${n}`),!r.trustExecutableConfig)throw new Error(`Refusing to execute Workspai config at ${n} without explicit trust. Use a data-only workspai.config.json file, pass --trust-config, or set WORKSPAI_TRUST_CONFIG=1.`);try{let l=await import(pathToFileURL(n).href),f$1=l.default||l;return f.debug(`Loaded Workspai config from ${o}`),f$1}catch(a){throw new Error(L(n,a))}}e=c$1.dirname(e);}return f.debug("No Workspai config file found, using defaults"),{}}function Me(t,r,e){return {author:e.author||r.workspace?.defaultAuthor||t.author,pythonVersion:e.pythonVersion||r.workspace?.pythonVersion||t.pythonVersion,defaultInstallMethod:e.defaultInstallMethod||r.workspace?.installMethod||t.defaultInstallMethod,defaultKit:e.defaultKit||r.projects?.defaultKit||t.defaultKit,skipGit:e.skipGit??r.projects?.skipGit??t.skipGit,license:e.license||t.license,testRapidKitPath:e.testRapidKitPath||t.testRapidKitPath}}function Ge(t){return a("DEV_PATH")||t.testRapidKitPath||void 0}function re(t,r){let e=t.indexOf(r);if(e>=0&&e+1<t.length)return t[e+1];let i=t.find(o=>o.startsWith(`${r}=`));if(i)return i.slice(r.length+1)}function ie(t){return t.includes("--here")}function U(t,r=g.cwd()){if(ie(t))return c$1.resolve(r);let e=re(t,"--output");if(e)return c$1.resolve(e)}function Ze(t,r=g.cwd()){let e=c$1.resolve(t),i=c$1.resolve(r),o=c$1.relative(i,e);return o.length>0&&!o.startsWith("..")&&!c$1.isAbsolute(o)?`cd ${o}`:`cd ${e}`}function et(t,r={}){let e=r.argv??[],i=r.outputParent??U(e,g.cwd());return h(t.trim(),{homeDir:r.homeDir,outputDir:i})}function tt(t,r,e={}){return t?c$1.resolve(t)===c$1.resolve(r)?true:e.outputParent===void 0:false}async function rt(t,r={}){let e=r.cwd??g.cwd(),i=r.homeDir??homedir(),o=U(t,e);if(o!==void 0)return o;let n=r.hasYes??(t.includes("--yes")||t.includes("-y"));if(!(r.interactive??(!n&&!!g.stdin.isTTY&&!b())))return;let l=g$1(i),{location:f}=await b$1([{type:"rawlist",name:"location",message:"Where should the workspace be created?",choices:[{value:"managed",label:"Managed home",hint:l},{value:"here",label:"Current directory",hint:e}],default:0}]);if(f==="here")return c$1.resolve(e)}var W="workspai.lifecycle-transaction",J=1,N="manifest.json",_="lifecycle-transaction-";async function w(t){try{return await s.lstat(t)}catch(r){if(r.code==="ENOENT")return null;throw r}}function K(t){let r=t?.code;return process.platform==="win32"&&(r==="EPERM"||r==="EINVAL"||r==="ENOSYS")}async function D(t){try{await t.sync();}catch(r){if(!K(r))throw r}}function oe(t){if(typeof t!="string"||!c$1.isAbsolute(t))throw new Error("Lifecycle transaction journal contains a non-absolute target path.")}function ne(t){if(typeof t!="string"||t.length===0||c$1.basename(t)!==t||t==="."||t==="..")throw new Error("Lifecycle transaction journal contains an unsafe backup reference.")}function ae(t){if(!t||typeof t!="object")throw new Error("Lifecycle transaction journal is not an object.");let r=t;if(r.kind!==W||r.version!==J)throw new Error("Lifecycle transaction journal has an unsupported format.");if(!Number.isInteger(r.ownerPid)||r.ownerPid<=0)throw new Error("Lifecycle transaction journal has an invalid owner process.");if(!["active","committed","rolling-back"].includes(r.phase))throw new Error("Lifecycle transaction journal has an invalid phase.");if(!Array.isArray(r.operations))throw new Error("Lifecycle transaction journal has invalid operations.");for(let e of r.operations){if(!e||typeof e!="object")throw new Error("Lifecycle transaction journal contains an invalid operation.");let i=e;if(oe(i.path),i.type!=="tree"){if(i.type!=="file"||!["absent","regular"].includes(i.state))throw new Error("Lifecycle transaction journal contains an invalid operation.");if(i.state==="regular"){if(!Number.isInteger(i.mode)||i.mode<0||i.mode>4095)throw new Error("Lifecycle transaction journal contains an invalid file mode.");ne(i.backup);}}}return r}async function x(t){let r;try{r=await s.open(t,constants.O_RDONLY);}catch(e){if(K(e))return;throw e}try{await D(r);}finally{await r.close();}}async function M(t,r){let e=c$1.join(c$1.dirname(t),`.${c$1.basename(t)}.${process.pid}.${E.randomUUID()}.tmp`);try{let i=await s.open(e,constants.O_WRONLY|constants.O_CREAT|constants.O_EXCL,384);try{await i.writeFile(r),await D(i);}finally{await i.close();}await s.rename(e,t),await x(c$1.dirname(t));}catch(i){throw await s.rm(e,{force:true}).catch(()=>{}),i}}async function G(t,r){await M(c$1.join(t,N),Buffer.from(`${JSON.stringify(r,null,2)}
2
- `));}async function V(t,r){let e=await w(t.path);if(e?.isSymbolicLink()||e&&!e.isFile())throw new Error(`Refusing to restore over a non-regular file: ${t.path}`);if(t.state==="absent"){e&&await s.unlink(t.path);return}if(!r)throw new Error(`Missing durable backup for: ${t.path}`);if(!await w(c$1.dirname(t.path)))return;let i=c$1.join(r,t.backup),o=await s.lstat(i).catch(f=>{if(f.code==="ENOENT"&&e?.isFile())return e;throw f});if(o===e)return;if(!o.isFile()||o.isSymbolicLink())throw new Error(`Lifecycle transaction backup is not a regular file: ${i}`);let n=await s.readFile(i),a=c$1.join(c$1.dirname(t.path),`.${c$1.basename(t.path)}.${process.pid}.${E.randomUUID()}.rollback`),l=await s.open(a,constants.O_WRONLY|constants.O_CREAT|constants.O_EXCL,t.mode);try{await l.writeFile(n),await l.chmod(t.mode),await D(l);}finally{await l.close();}try{await s.rename(a,t.path),await x(c$1.dirname(t.path));}catch(f){throw await s.rm(a,{force:true}).catch(()=>{}),f}}async function Y(t){let r=await w(t.path);if(r){if(r.isSymbolicLink()||!r.isDirectory())throw new Error(`Refusing to remove a non-directory owned tree: ${t.path}`);await s.rm(t.path,{recursive:true});}}async function se(t,r){let e=[];for(let i of [...t].reverse())try{i.type==="file"?await V(i,r):await Y(i);}catch(o){e.push(o);}if(e.length>0)throw new AggregateError(e,"Lifecycle transaction rollback failed.")}var j=class t{constructor(r){this.transactionDirectory=r;this.journalPath=r?c$1.join(r,N):void 0;}transactionDirectory;journalPath;actions=[];durableOperations=[];phase="active";finished=false;static async create(r={}){if(!r.journalDirectory)return new t;let e=c$1.resolve(r.journalDirectory);await s.mkdir(e,{recursive:true});let i=await s.mkdtemp(c$1.join(e,_));await x(e);let o=new t(i);return await o.persist(),o}assertActive(){if(this.finished||this.phase!=="active")throw new Error("Lifecycle transaction is no longer active.")}async persist(r=this.durableOperations){this.transactionDirectory&&await G(this.transactionDirectory,{kind:W,version:J,ownerPid:process.pid,phase:this.phase,operations:r});}async addDurableOperation(r){let e=[...this.durableOperations,r];await this.persist(e),this.durableOperations.push(r),this.actions.push(r);}async captureFile(r){this.assertActive();let e=c$1.resolve(r),i=await w(e);if(!i){await this.addDurableOperation({type:"file",path:e,state:"absent"});return}if(i.isSymbolicLink()||!i.isFile())throw new Error(`Lifecycle transaction can only capture regular files: ${e}`);let o;try{o=await s.open(e,constants.O_RDONLY|(constants.O_NOFOLLOW??0));}catch(n){throw n.code==="ENOENT"?new Error(`File changed while being captured: ${e}`,{cause:n}):n}try{let n=await o.stat();if(!n.isFile())throw new Error(`Lifecycle transaction can only capture regular files: ${e}`);let a=await o.readFile(),l=n.mode&4095;if(this.transactionDirectory){let f=`file-${this.durableOperations.length}-${E.randomUUID()}.bin`;await M(c$1.join(this.transactionDirectory,f),a),await this.addDurableOperation({type:"file",path:e,state:"regular",mode:l,backup:f});}else {let f=`memory-${this.durableOperations.length}`,y={type:"file",path:e,state:"regular",mode:l,backup:f};this.durableOperations.push(y),this.actions.push({type:"compensation",run:async()=>{let d=await w(e);if(d?.isSymbolicLink()||d&&!d.isFile())throw new Error(`Refusing to restore over a non-regular file: ${e}`);let b=c$1.join(c$1.dirname(e),`.${c$1.basename(e)}.${process.pid}.${E.randomUUID()}.rollback`);await s.writeFile(b,a,{flag:"wx",mode:l}),await s.chmod(b,l);try{await s.rename(b,e);}catch(H){throw await s.rm(b,{force:true}).catch(()=>{}),H}}});}}finally{await o.close();}}async captureOwnedTree(r){this.assertActive();let e=c$1.resolve(r),i=await w(e);if(!i)return await this.addDurableOperation({type:"tree",path:e}),true;if(i.isSymbolicLink()||!i.isDirectory())throw new Error(`Lifecycle transaction tree must be absent or a directory: ${e}`);return false}registerCompensation(r){this.assertActive(),this.actions.push({type:"compensation",run:async()=>r()});}async commit(){if(!this.finished){this.assertActive(),this.phase="committed";try{await this.persist(),this.finished=true,this.actions.length=0,this.transactionDirectory&&await s.rm(this.transactionDirectory,{recursive:true}).catch(()=>{});}catch(r){throw this.phase="active",r}}}async rollback(){if(this.finished||this.phase==="committed")return;this.assertActive(),this.phase="rolling-back";try{await this.persist();}catch(e){throw this.phase="active",e}let r=[];for(let e of [...this.actions].reverse())try{e.type==="compensation"?await e.run():e.type==="file"?await V(e,this.transactionDirectory):await Y(e);}catch(i){r.push(i);}if(r.length>0)throw new AggregateError(r,"Lifecycle transaction rollback failed.");this.finished=true,this.transactionDirectory&&await s.rm(this.transactionDirectory,{recursive:true});}};async function lt(t={}){return j.create(t)}async function ft(t){let r=c$1.resolve(t),e=await s.readdir(r,{withFileTypes:true}).catch(n=>{if(n.code==="ENOENT")return [];throw n}),i=[],o=[];for(let n of e){if(!n.isDirectory()||!n.name.startsWith(_))continue;let a=c$1.join(r,n.name);try{let l;try{l=await s.readFile(c$1.join(a,N),"utf8");}catch(d){if(d.code==="ENOENT")continue;throw d}let f=ae(JSON.parse(l));if(f.phase==="committed"){await s.rm(a,{recursive:true});continue}let y=false;try{process.kill(f.ownerPid,0),y=true;}catch(d){y=d.code==="EPERM";}if(y)continue;f.phase==="active"&&(f.phase="rolling-back",await G(a,f)),await se(f.operations,a),await s.rm(a,{recursive:true}),i.push(a);}catch(l){o.push(l);}}if(o.length>0)throw new AggregateError(o,"Lifecycle transaction recovery failed.");return i}export{B as a,_e as b,Ke as c,Me as d,Ge as e,Ze as f,et as g,tt as h,rt as i,lt as j,ft as k};
@@ -1,8 +0,0 @@
1
- import {a as a$1}from'./chunk-QA5BGEQW.js';import {a as a$4}from'./chunk-22DT744Z.js';import {a as a$2,e}from'./chunk-WPEEC5BX.js';import {f,c,g}from'./chunk-HDURFXW5.js';import {a}from'./chunk-N7DV5L7C.js';import a$3 from'chalk';import se from'fs';import b from'path';import {execa}from'execa';var G="autopilot-release-last-run.json",V="autopilot-release.json";function j(r){try{return JSON.parse(r)}catch{return null}}function Re(r){let e=b.resolve(r),n=a$4(e);if(!n)throw new Error("No Workspai workspace found in current directory or parents");return n}async function S(r,e){let n=process.argv[1];if(!n)return {exitCode:1,stdout:"",stderr:"RapidKit entrypoint is unavailable for autopilot execution.",crashed:true};try{let o=await execa(process.execPath,[n,...r],{cwd:e,reject:false,env:{...process.env,RAPIDKIT_AUTOPILOT_CHILD:"1"}});return {exitCode:Number(o.exitCode??1),stdout:o.stdout,stderr:o.stderr,crashed:false}}catch(o){return {exitCode:1,stdout:"",stderr:o instanceof Error?o.message:String(o),crashed:true}}}function re(r){let e=r.toLowerCase();return e==="pass"?"pass":e==="warn"?"warn":e==="fail"?"fail":"warn"}function oe(r){return r.summary.failed>0||r.gates.results.some(e=>e.status==="fail")?"fail":r.gates.results.some(e=>e.status==="warn")?"warn":"pass"}function Se(r){let e=r.filter(o=>o.status!=="skipped");if(e.length===0)return 100;let n=e.reduce((o,t)=>t.status==="pass"?o+1:t.status==="warn"?o+.6:o,0);return Math.round(n/e.length*100)}function be(r){return r.executionError?["Re-run: npx workspai autopilot release --mode audit --json","Inspect .workspai/reports/autopilot-release-last-run.json for execution failure details"]:r.blockers.length>0?["Run: npx workspai doctor workspace --plan","Run: npx workspai readiness --json --strict","Run: npx workspai workspace run test --affected --strict"]:r.hasWarnings&&r.mode!=="enforce"?["Review warning-level findings in autopilot report","Optionally run: npx workspai autopilot release --mode safe-fix"]:["Workspace is release-ready based on current autopilot policy"]}async function xe(r,e){await se.promises.mkdir(b.dirname(r),{recursive:true}),await se.promises.writeFile(r,`${JSON.stringify(e,null,2)}
2
- `,"utf-8");}async function Me(r){let e$1=Re(r.workspacePath),n=r.mode,o=[],t=[],F,H,D,q,Q,X=0,f$1=false,T=0,L=0,N=-1,W=-1,c$1="skipped",d="skipped",ae=n==="enforce"?["doctor","workspace","--json","--strict"]:["doctor","workspace","--json","--ci"];if(r.skipPipelineStages)o.push({name:"doctor-workspace",status:"skipped",durationMs:0,summary:"doctor stage skipped (pipeline already executed)"},{name:"analyze",status:"skipped",durationMs:0,summary:"analyze stage skipped (pipeline already executed)"},{name:"readiness",status:"skipped",durationMs:0,summary:"readiness stage skipped (pipeline already executed)"});else {let s=Date.now(),l=await S([...ae],e$1),m=Date.now()-s;if(c$1="pass",l.crashed)c$1="fail",f$1=true,t.push(`doctor workspace execution error: ${l.stderr||"unknown error"}`);else if(l.exitCode===1)c$1="fail",t.push("doctor workspace command failed or reported errors");else if(l.exitCode===2)c$1="warn",t.push("doctor workspace reported warnings");else if(l.exitCode!==0)c$1="fail",t.push("doctor workspace command failed");else {let p=j(l.stdout),E=p&&p.healthScore&&typeof p.healthScore=="object"?p.healthScore:{},k=Number(E.errors??0),$=Number(E.warnings??0);Number.isFinite(k)&&k>0?(c$1="fail",t.push(`doctor workspace reports ${k} error(s)`)):Number.isFinite($)&&$>0&&(c$1="warn");}N=o.push({name:"doctor-workspace",status:c$1,durationMs:m,summary:c$1==="pass"?"doctor workspace passed":c$1==="warn"?"doctor workspace reported warnings":"doctor workspace reported errors"})-1,n==="enforce"&&c$1==="warn"&&t.push("doctor workspace reported warnings under enforce mode");let g=Date.now(),w=await S(["analyze","--json"],e$1),v=Date.now()-g;H=await f(e$1,a.analyze)??c(e$1,a.analyze);let i="pass";if(w.crashed)i="fail",f$1=true,t.push(`analyze execution error: ${w.stderr||"unknown error"}`);else if(w.exitCode!==0)i=w.exitCode===2?"warn":"fail",t.push(w.exitCode===2?"analyze reported needs-attention verdict":"analyze command failed or reported blocked verdict");else {let p=j(w.stdout),E=p&&p.summary&&typeof p.summary=="object"?p.summary:{},k=String(E.verdict??"ready");k==="blocked"?(i="fail",t.push("analyze reported blocked verdict")):k==="needs-attention"&&(i="warn");}o.push({name:"analyze",status:i,durationMs:v,summary:i==="pass"?"analyze passed":i==="warn"?"analyze reported needs-attention":"analyze reported blocked verdict"}),n==="enforce"&&i==="warn"&&t.push("analyze reported warnings under enforce mode");let P=Date.now(),h=await S(["readiness","--json"],e$1),y=Date.now()-P;if(d="fail",h.crashed)d="fail",f$1=true,t.push(`readiness execution error: ${h.stderr||"unknown error"}`);else if(h.exitCode!==0)d="fail",t.push("readiness command failed");else {let p=j(h.stdout),E=String(p?.overallStatus??"fail");if(d=re(E),typeof p?.evidencePath=="string"&&p.evidencePath.trim().length>0&&(F=p.evidencePath),d==="fail"){let k=Array.isArray(p?.blockingReasons)?p?.blockingReasons.filter($=>typeof $=="string"):[];k.length>0?t.push(...k.map($=>`readiness: ${$}`)):t.push("readiness overall status is fail");}}W=o.push({name:"readiness",status:d,durationMs:y,summary:`readiness overall status is ${d}`})-1,n==="enforce"&&d==="warn"&&t.push("readiness reported warnings under enforce mode");}let ne=Date.now(),z=await S(["doctor","workspace","--plan","--json"],e$1),ie=Date.now()-ne,R="warn";if(z.crashed)R="fail",f$1=true,t.push(`doctor remediation plan execution error: ${z.stderr||"unknown error"}`);else if(z.exitCode!==0)R="fail",t.push("doctor remediation plan command failed");else {let s=j(z.stdout),l=s&&s.remediationPlan&&typeof s.remediationPlan=="object"?s.remediationPlan:null,m=Number(l?.totalSteps??0),g=Number(l?.executableSteps??0);Number.isFinite(m)&&m>0&&(T=m),Number.isFinite(g)&&g>0&&(L=g),Number.isFinite(m)&&m===0?R="pass":R="warn";}if(o.push({name:"remediation-plan",status:R,durationMs:ie,summary:R==="pass"?"no remediation steps required":"remediation steps available"}),n==="enforce"&&R==="warn"&&t.push("remediation plan has pending steps under enforce mode"),n==="safe-fix"){let s=Date.now(),l=await S(["doctor","workspace","--apply"],e$1),m=Date.now()-s,g=l.crashed||l.exitCode!==0?"fail":"pass";if(l.crashed)f$1=true,t.push(`doctor remediation apply execution error: ${l.stderr||"unknown error"}`);else if(g==="fail")t.push("doctor remediation apply failed");else {X=L>0?L:T>0?T:1;let w=await S(["doctor","workspace","--json"],e$1);if(w.crashed)c$1="fail",f$1=true,t.push(`post-apply doctor execution error: ${w.stderr||"unknown error"}`);else if(w.exitCode!==0)c$1="fail",t.push("post-apply doctor workspace command failed");else {let i=j(w.stdout),P=i&&i.healthScore&&typeof i.healthScore=="object"?i.healthScore:{},h=Number(P.errors??0),y=Number(P.warnings??0);Number.isFinite(h)&&h>0?(c$1="fail",t.push(`post-apply doctor reports ${h} error(s)`)):Number.isFinite(y)&&y>0?c$1="warn":c$1="pass";}N>=0&&(o[N].status=c$1,o[N].summary=`doctor workspace post-apply status is ${c$1}`);let v=await S(["readiness","--json"],e$1);if(v.crashed)d="fail",f$1=true,t.push(`post-apply readiness execution error: ${v.stderr||"unknown error"}`);else if(v.exitCode!==0)d="fail",t.push("post-apply readiness command failed");else {let i=j(v.stdout),P=String(i?.overallStatus??"fail");if(d=re(P),typeof i?.evidencePath=="string"&&i.evidencePath.trim().length>0&&(F=i.evidencePath),d==="fail"){let h=Array.isArray(i?.blockingReasons)?i?.blockingReasons.filter(y=>typeof y=="string"):[];h.length>0?t.push(...h.map(y=>`post-apply readiness: ${y}`)):t.push("post-apply readiness overall status is fail");}}W>=0&&(o[W].status=d,o[W].summary=`readiness post-apply overall status is ${d}`);}o.push({name:"remediation-apply",status:g,durationMs:m,summary:g==="pass"?"safe remediation apply completed":"safe remediation apply failed"});}else o.push({name:"remediation-apply",status:"skipped",durationMs:0,summary:"remediation apply is skipped for this mode"});let le=Date.now(),x="pass",A="workspace test/build completed for selected projects";try{let s=await a$1({workspacePath:e$1,stage:"test",affected:true,since:r.since,parallel:r.parallel,maxWorkers:r.maxWorkers,strict:true,json:true,enforceGates:false});D=c(e$1,b.join(".workspai","reports",a$2)),await e(e$1,s),q=D;let l=oe(s);if(l==="fail")x="fail",A="workspace test stage failed for selected projects",t.push("workspace run test failed for selected projects");else {l==="warn"&&(x="warn",A="workspace test stage completed with warnings",n==="enforce"&&t.push("workspace run test reported warnings under enforce mode"));let m=await a$1({workspacePath:e$1,stage:"build",affected:true,since:r.since,parallel:r.parallel,maxWorkers:r.maxWorkers,strict:true,json:true,enforceGates:false});await e(e$1,m),Q=D;let g=oe(m);g==="fail"?(x="fail",A="workspace build stage failed for selected projects",t.push("workspace run build failed for selected projects")):g==="warn"&&(x="warn",A="workspace test/build completed with warnings",n==="enforce"&&t.push("workspace run test/build reported warnings under enforce mode"));}}catch(s){x="fail",A="workspace test/build orchestration failed",f$1=true,t.push(`workspace run orchestration error: ${s instanceof Error?s.message:String(s)}`);}let ce=Date.now()-le;o.push({name:"workspace-run-test-build",status:x,durationMs:ce,summary:A});let M=o.filter(s=>s.status==="warn").length,pe=o.filter(s=>s.status==="fail").length,_=n==="enforce"?o.some(s=>s.status==="warn"||s.status==="fail"):pe>0,C=[...new Set(t)],O=f$1||_?Math.max(1,C.length):0,ue=O>0?C:[],de=O===0?C:[],fe=Se(o),me=f$1||_?"blocked":M>0?"partial":"approved",ge=f$1?3:_?1:M>0?2:0,we=be({mode:n,executionError:f$1,blockers:C,hasWarnings:M>0}),Y=c(e$1,".workspai/reports"),he=b.join(Y,G),ke=b.join(Y,V),u={schemaVersion:"autopilot-release-v1",generatedAt:new Date().toISOString(),workspacePath:e$1,mode:n,summary:{releaseScore:fe,verdict:me,blockers:O,warnings:M,safeFixesApplied:X,manualActions:C.length,exitCode:ge},stages:o,blockingReasons:ue,warningReasons:de,nextActions:we,artifacts:{reportPath:he,aliasEvidencePath:ke,analyzeEvidencePath:H,readinessEvidencePath:F,workspaceRunEvidencePath:D,workspaceRunTestPath:q,workspaceRunBuildPath:Q},enterpriseControls:{jsonReady:true,evidencePath:`.workspai/reports/${G}`,aliasEvidencePath:`.workspai/reports/${V}`}};if(await g(e$1,`.workspai/reports/${G}`,u),await g(e$1,`.workspai/reports/${V}`,u),r.output&&await xe(b.resolve(r.output),u),!r.json){console.log(a$3.bold.cyan(`
3
- \u{1F680} Workspai Autopilot Release
4
- `)),console.log(a$3.bold(`Workspace: ${a$3.cyan(b.basename(e$1))}`)),console.log(a$3.gray(`Path: ${e$1}`)),console.log(a$3.white(`Mode: ${n}`)),console.log(a$3.white(`Verdict: ${u.summary.verdict==="approved"?a$3.green("approved"):u.summary.verdict==="partial"?a$3.yellow("partial"):a$3.red("blocked")}`)),console.log(a$3.white(`Release score: ${u.summary.releaseScore}`));for(let s of u.stages){let l=s.status==="pass"?a$3.green("PASS"):s.status==="warn"?a$3.yellow("WARN"):s.status==="skipped"?a$3.gray("SKIP"):a$3.red("FAIL");console.log(` - ${s.name}: ${l} ${s.summary} (${s.durationMs}ms)`);}if(u.blockingReasons.length>0){console.log(a$3.bold.red(`
5
- Blocking reasons:`));for(let s of u.blockingReasons)console.log(a$3.red(` - ${s}`));}if(u.warningReasons.length>0){console.log(a$3.bold.yellow(`
6
- Warning reasons:`));for(let s of u.warningReasons)console.log(a$3.yellow(` - ${s}`));}if(u.nextActions.length>0){console.log(a$3.bold(`
7
- Next actions:`));for(let s of u.nextActions)console.log(a$3.gray(` - ${s}`));}console.log(a$3.gray(`
8
- Report: ${u.artifacts.reportPath}`));}return u}export{G as a,V as b,Me as c};