@smartmemory/compose 0.1.7-beta → 0.1.9-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -5
- package/bin/compose.js +294 -34
- package/bin/git-hooks/post-commit.template +2 -1
- package/bin/git-hooks/pre-push.template +2 -1
- package/dist/assets/{_baseUniq-D-avYfn5.js → _baseUniq-3jW4HAOf.js} +1 -1
- package/dist/assets/{arc-BC4dfQ-X.js → arc-DzzDimyd.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-BZmFXnGI.js → architectureDiagram-Q4EWVU46-CtAgwORz.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-DlfWSuux.js → blockDiagram-DXYQGD6D-Bryby0c_.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-Y__uJrRx.js → c4Diagram-AHTNJAMY-C7N9RTJ8.js} +1 -1
- package/dist/assets/channel-DDkv7DUd.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-BfMePfTp.js → chunk-4BX2VUAB-wijkFgZY.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-BdlMSdEA.js → chunk-4TB4RGXK-zdSZGRS2.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-vrQHZTdv.js → chunk-55IACEB6-6zqzTZQQ.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-B8wioVlW.js → chunk-EDXVE4YY-frd1Vwf-.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-Cd6Hrux2.js → chunk-FMBD7UC4-CdkRK5Hx.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-CfrhdQXY.js → chunk-OYMX7WX6-C6bMB0cf.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-B9JQerOU.js → chunk-QZHKN3VN-4vsxN3jq.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-DFN9X99H.js → chunk-YZCP3GAM-DbNARKip.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-J6ZTeCbW.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-J6ZTeCbW.js +1 -0
- package/dist/assets/clone-5MVZ89iV.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-BAn0ap_E.js → cose-bilkent-S5V4N54A-BpXeV7Vj.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-DyxnVq1g.js → dagre-KV5264BT-DQLu_W8r.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-XCrzqski.js → diagram-5BDNPKRD-skaOoe5A.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-MBCAXft_.js → diagram-G4DWMVQ6-DezlfFH4.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-DbtB2yS6.js → diagram-MMDJMWI5-BUu-v-wT.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-Bb5NzX61.js → diagram-TYMM5635-CziQ6LPs.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-CpIeCOh2.js → erDiagram-SMLLAGMA-BsAyOVTI.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-CHyoKnhW.js → flowDiagram-DWJPFMVM-CbYWJOLq.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-DErKteO_.js → ganttDiagram-T4ZO3ILL-CAwgDkLl.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-KFVAtj2F.js → gitGraphDiagram-UUTBAWPF-DK4RlkjO.js} +1 -1
- package/dist/assets/{graph-CRnO_ifT.js → graph-orv1XHGx.js} +1 -1
- package/dist/assets/{index-DkRKLuNr.js → index-Ceywghsu.js} +143 -143
- package/dist/assets/{infoDiagram-42DDH7IO-BZFnuSp5.js → infoDiagram-42DDH7IO-DQyA75sK.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-4Xe2Szde.js → ishikawaDiagram-UXIWVN3A-C-F_5q4k.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-CZRByfS-.js → journeyDiagram-VCZTEJTY-Bj8UIvK-.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-B95sk6Fk.js → kanban-definition-6JOO6SKY-DZYr8Dp1.js} +1 -1
- package/dist/assets/{layout-BqNQzxWT.js → layout-CBaTKjpX.js} +1 -1
- package/dist/assets/{linear-CUh7qb64.js → linear-j1sI_SiN.js} +1 -1
- package/dist/assets/{min-wXgOS3ig.js → min-DtJISjld.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-DB6iaAbO.js → mindmap-definition-QFDTVHPH-Bulb64RS.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-CHkZHrTW.js → pieDiagram-DEJITSTG-D11keQxr.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-DoTEO8e3.js → quadrantDiagram-34T5L4WZ-BEcWQiEG.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-Dn8peXYp.js → requirementDiagram-MS252O5E-Cbp23uDf.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-DRXs6Ipb.js → sankeyDiagram-XADWPNL6-Dae1hMc5.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-wBBYZ0aq.js → sequenceDiagram-FGHM5R23-C16abORi.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-DPlBNGmf.js → stateDiagram-FHFEXIEX-CbEtfhbx.js} +1 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-CyY84hEA.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-CbbyTlHk.js → timeline-definition-GMOUNBTQ-BV7JTNMI.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-Bj4GaFfj.js → vennDiagram-DHZGUBPP-DBZiT48j.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-RtNzq8KU.js → wardley-RL74JXVD-Cc8uoiL3.js} +37 -37
- package/dist/assets/{wardleyDiagram-NUSXRM2D-CDfE3zSj.js → wardleyDiagram-NUSXRM2D-DEYcWGo5.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-CZXHHYD5.js → xychartDiagram-5P7HB3ND-bFhLXv2b.js} +1 -1
- package/dist/index.html +1 -1
- package/lib/build.js +193 -19
- package/lib/completion-writer.js +7 -4
- package/lib/deps.js +17 -6
- package/lib/discover-workspaces.js +109 -0
- package/lib/feature-events.js +3 -0
- package/lib/feature-writer.js +34 -22
- package/lib/followup-writer.js +556 -0
- package/lib/mcp-enforcement.js +173 -0
- package/lib/migrate-roadmap.js +4 -1
- package/lib/project-paths.js +36 -0
- package/lib/resolve-workspace.js +166 -0
- package/lib/review-lenses.js +23 -8
- package/lib/review-normalize.js +42 -3
- package/lib/roadmap-drift.js +54 -0
- package/lib/roadmap-gen.js +297 -27
- package/lib/roadmap-preservers.js +353 -0
- package/lib/step-prompt.js +15 -0
- package/lib/triage.js +2 -1
- package/lib/version-check.js +110 -0
- package/package.json +1 -2
- package/server/compose-mcp-tools.js +44 -8
- package/server/compose-mcp.js +66 -1
- package/server/project-root.js +4 -0
- package/server/vision-routes.js +51 -2
- package/templates/ROADMAP.md +6 -0
- package/dist/assets/channel-LRG9kHqJ.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BC9a6pDE.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-BC9a6pDE.js +0 -1
- package/dist/assets/clone-dRxgFrBv.js +0 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-BW0ezXb4.js +0 -1
package/README.md
CHANGED
|
@@ -34,9 +34,20 @@ compose build TODO-1
|
|
|
34
34
|
|
|
35
35
|
Prerequisites: Node.js 18+ and `stratum-mcp` on PATH (`pip install stratum`). Codex steps additionally need the OpenAI `codex` CLI. Full prereqs in [docs/install.md](docs/install.md).
|
|
36
36
|
|
|
37
|
+
The package is published to npm as `@smartmemory/compose`. Pick one install style:
|
|
38
|
+
|
|
39
|
+
**Option A — npm (recommended for users):**
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g @smartmemory/compose
|
|
43
|
+
compose setup # global skill + stratum-mcp registration
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Option B — git clone (for development):**
|
|
47
|
+
|
|
37
48
|
```bash
|
|
38
49
|
git clone https://github.com/smartmemory/compose.git && cd compose && npm install
|
|
39
|
-
npx compose setup
|
|
50
|
+
npx @smartmemory/compose setup # or: node bin/compose.js setup
|
|
40
51
|
ln -s "$(pwd)/bin/compose.js" ~/bin/compose && chmod +x ~/bin/compose # optional: bare `compose` command
|
|
41
52
|
```
|
|
42
53
|
|
|
@@ -44,15 +55,31 @@ Then in your project:
|
|
|
44
55
|
|
|
45
56
|
```bash
|
|
46
57
|
cd /path/to/your/project
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
compose init # writes .compose/, registers MCP, scaffolds ROADMAP and pipeline specs
|
|
59
|
+
compose new "what you want to build"
|
|
49
60
|
```
|
|
50
61
|
|
|
51
62
|
Add an isolated feature to an existing project:
|
|
52
63
|
|
|
53
64
|
```bash
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
compose feature AUTH-1 "JWT middleware with refresh tokens"
|
|
66
|
+
compose build AUTH-1
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Upgrading
|
|
70
|
+
|
|
71
|
+
One command — auto-detects whether compose was installed via npm or git clone:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
compose update
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For npm installs, this runs `npm install -g @smartmemory/compose@latest`. For git clones, it runs `git pull --ff-only && npm install`. Either way it then refreshes the global skill and (if invoked from inside a Compose project) re-runs `compose init` to refresh `.mcp.json` and pipeline templates. Use `compose update --force` to bypass the dirty-tree check on git clones.
|
|
78
|
+
|
|
79
|
+
Check what you're running:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
compose --version
|
|
56
83
|
```
|
|
57
84
|
|
|
58
85
|
## Documentation
|
package/bin/compose.js
CHANGED
|
@@ -10,20 +10,73 @@
|
|
|
10
10
|
* compose fix — headless bug-fix lifecycle runner (pipelines/bug-fix.stratum.yaml)
|
|
11
11
|
*/
|
|
12
12
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync, rmSync, readdirSync } from 'fs'
|
|
13
|
-
import { resolve, join, basename, dirname } from 'path'
|
|
13
|
+
import { resolve, join, basename, dirname, sep } from 'path'
|
|
14
14
|
import { homedir } from 'os'
|
|
15
15
|
import { spawn, spawnSync } from 'child_process'
|
|
16
16
|
import { fileURLToPath } from 'url'
|
|
17
17
|
import { findProjectRoot } from '../server/find-root.js'
|
|
18
|
+
import { resolveWorkspace, getWorkspaceFlag } from '../lib/resolve-workspace.js'
|
|
18
19
|
|
|
19
20
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
20
21
|
const PACKAGE_ROOT = resolve(__dirname, '..')
|
|
21
22
|
|
|
23
|
+
function dieOnWorkspaceError(err) {
|
|
24
|
+
switch (err.code) {
|
|
25
|
+
case 'WorkspaceAmbiguous':
|
|
26
|
+
console.error('Multiple workspaces match cwd. Add --workspace=<id> or set COMPOSE_TARGET:')
|
|
27
|
+
for (const c of err.candidates) console.error(` --workspace=${c.id} (${c.root})`)
|
|
28
|
+
process.exit(1)
|
|
29
|
+
case 'WorkspaceIdCollision':
|
|
30
|
+
console.error(`workspaceId "${err.id}" is used by multiple roots:`)
|
|
31
|
+
for (const r of err.roots) console.error(` ${r}`)
|
|
32
|
+
console.error('Set an explicit workspaceId in each .compose/compose.json.')
|
|
33
|
+
process.exit(1)
|
|
34
|
+
case 'WorkspaceUnknown':
|
|
35
|
+
// err.message may be the path-doesn't-exist form; prefer it when richer.
|
|
36
|
+
console.error(err.message.includes('does not exist') ? err.message : `Unknown workspace: ${err.id}. Run \`compose doctor\` to list candidates.`)
|
|
37
|
+
process.exit(1)
|
|
38
|
+
case 'WorkspaceUnset':
|
|
39
|
+
console.error('No compose workspace found from the current directory.')
|
|
40
|
+
console.error('Run `compose init` to scaffold one, or cd into a project that has a .compose/ directory.')
|
|
41
|
+
process.exit(1)
|
|
42
|
+
case 'WorkspaceDiscoveryTooBroad':
|
|
43
|
+
console.error('Workspace discovery exceeded its bound from anchor.')
|
|
44
|
+
console.error('Set COMPOSE_TARGET=/absolute/path/to/workspace to bypass discovery.')
|
|
45
|
+
process.exit(1)
|
|
46
|
+
default:
|
|
47
|
+
throw err
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Cache the resolved cwd for the lifetime of this CLI process. resolveCwdWithWorkspace
|
|
52
|
+
// strips --workspace from args on first call (via getWorkspaceFlag splice), so a second
|
|
53
|
+
// call would re-resolve without the hint. Cache prevents this; ensures auto-init paths
|
|
54
|
+
// (runInit re-entry from build/fix/import/new) see the same workspace.
|
|
55
|
+
let _resolvedCwdCache = null
|
|
56
|
+
|
|
57
|
+
function resolveCwdWithWorkspace(args) {
|
|
58
|
+
if (_resolvedCwdCache !== null) return _resolvedCwdCache
|
|
59
|
+
let wsId = getWorkspaceFlag(args)
|
|
60
|
+
// Legacy hooks may pass the unsubstituted token literally — treat as absent.
|
|
61
|
+
if (wsId === '__COMPOSE_WORKSPACE_ID__') {
|
|
62
|
+
console.warn('[compose] hook predates workspace-aware install — re-run `compose hooks install`')
|
|
63
|
+
wsId = null
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const ws = resolveWorkspace({ workspaceId: wsId })
|
|
67
|
+
_resolvedCwdCache = ws.root
|
|
68
|
+
return ws.root
|
|
69
|
+
} catch (err) {
|
|
70
|
+
dieOnWorkspaceError(err)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
22
74
|
// ---------------------------------------------------------------------------
|
|
23
75
|
// --team flag (COMP-TEAMS)
|
|
24
76
|
// ---------------------------------------------------------------------------
|
|
25
77
|
import { parseTeamFlag } from '../lib/team-flag.js';
|
|
26
|
-
import { loadDeps, checkExternalSkills, printDepReport } from '../lib/deps.js';
|
|
78
|
+
import { loadDeps, checkExternalSkills, printDepReport, buildDepReport } from '../lib/deps.js';
|
|
79
|
+
import { checkLatestVersion } from '../lib/version-check.js';
|
|
27
80
|
|
|
28
81
|
const [,, cmd, ...args] = process.argv
|
|
29
82
|
|
|
@@ -31,6 +84,20 @@ const [,, cmd, ...args] = process.argv
|
|
|
31
84
|
// Help
|
|
32
85
|
// ---------------------------------------------------------------------------
|
|
33
86
|
|
|
87
|
+
if (cmd === '--version' || cmd === '-V' || cmd === 'version') {
|
|
88
|
+
const pkgPath = join(PACKAGE_ROOT, 'package.json')
|
|
89
|
+
let version = 'unknown'
|
|
90
|
+
try { version = JSON.parse(readFileSync(pkgPath, 'utf-8')).version } catch {}
|
|
91
|
+
console.log(`compose ${version}`)
|
|
92
|
+
const gitDir = join(PACKAGE_ROOT, '.git')
|
|
93
|
+
if (existsSync(gitDir)) {
|
|
94
|
+
const sha = spawnSync('git', ['-C', PACKAGE_ROOT, 'rev-parse', '--short', 'HEAD'], { encoding: 'utf-8' })
|
|
95
|
+
if (sha.status === 0) console.log(` git: ${sha.stdout.trim()}`)
|
|
96
|
+
}
|
|
97
|
+
console.log(` root: ${PACKAGE_ROOT}`)
|
|
98
|
+
process.exit(0)
|
|
99
|
+
}
|
|
100
|
+
|
|
34
101
|
if (!cmd || cmd === '--help' || cmd === '-h') {
|
|
35
102
|
console.log('Usage: compose <command>')
|
|
36
103
|
console.log('')
|
|
@@ -49,7 +116,9 @@ if (!cmd || cmd === '--help' || cmd === '-h') {
|
|
|
49
116
|
console.log(' qa-scope Show affected routes from a feature\'s changed files')
|
|
50
117
|
console.log(' init Initialize Compose in the current project')
|
|
51
118
|
console.log(' setup Install global skill + register stratum-mcp')
|
|
119
|
+
console.log(' update Pull latest compose, reinstall deps, refresh global skill')
|
|
52
120
|
console.log(' doctor Check external skill dependencies')
|
|
121
|
+
console.log(' --version Print compose version, git SHA, and install root')
|
|
53
122
|
process.exit(0)
|
|
54
123
|
}
|
|
55
124
|
|
|
@@ -195,10 +264,11 @@ function syncSkills(agents) {
|
|
|
195
264
|
// compose doctor — re-run the external dep check
|
|
196
265
|
// ---------------------------------------------------------------------------
|
|
197
266
|
|
|
198
|
-
function runDoctor(flags = []) {
|
|
267
|
+
async function runDoctor(flags = []) {
|
|
199
268
|
const json = flags.includes('--json')
|
|
200
269
|
const strict = flags.includes('--strict')
|
|
201
270
|
const verbose = flags.includes('--verbose') || flags.includes('-v')
|
|
271
|
+
const refresh = flags.includes('--refresh-versions')
|
|
202
272
|
|
|
203
273
|
const deps = loadDeps(PACKAGE_ROOT)
|
|
204
274
|
if (!deps) {
|
|
@@ -206,8 +276,39 @@ function runDoctor(flags = []) {
|
|
|
206
276
|
process.exit(1)
|
|
207
277
|
}
|
|
208
278
|
const result = checkExternalSkills(deps)
|
|
209
|
-
const allRequiredPresent = printDepReport(result, { json, verbose })
|
|
210
279
|
|
|
280
|
+
// Version drift check — never fails the doctor run.
|
|
281
|
+
const currentVersion = (() => {
|
|
282
|
+
try { return JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf-8')).version }
|
|
283
|
+
catch { return null }
|
|
284
|
+
})()
|
|
285
|
+
const versionInfo = await checkLatestVersion(currentVersion, { force: refresh })
|
|
286
|
+
|
|
287
|
+
if (json) {
|
|
288
|
+
// Single top-level JSON document — the deps report and the version block share one root
|
|
289
|
+
// so consumers like `JSON.parse(stdout)` work. (Previously two concatenated objects.)
|
|
290
|
+
const report = buildDepReport(result)
|
|
291
|
+
console.log(JSON.stringify({ ...report, version: versionInfo }, null, 2))
|
|
292
|
+
const allRequiredPresent = result.missing.every(d => d.optional)
|
|
293
|
+
if (strict && !allRequiredPresent) process.exit(1)
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Version section — printed first so it's visible above long dep lists.
|
|
298
|
+
console.log('Version:')
|
|
299
|
+
console.log(` installed: ${currentVersion ?? 'unknown'}`)
|
|
300
|
+
if (versionInfo) {
|
|
301
|
+
console.log(` latest: ${versionInfo.latest} (${versionInfo.source})`)
|
|
302
|
+
if (versionInfo.behind) {
|
|
303
|
+
console.log(` ⚠ behind — run: compose update`)
|
|
304
|
+
} else {
|
|
305
|
+
console.log(` ✓ up to date`)
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
console.log(` latest: unavailable (registry unreachable or cache missing)`)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const allRequiredPresent = printDepReport(result, { json: false, verbose })
|
|
211
312
|
if (strict && !allRequiredPresent) process.exit(1)
|
|
212
313
|
}
|
|
213
314
|
|
|
@@ -218,6 +319,10 @@ function runDoctor(flags = []) {
|
|
|
218
319
|
async function runInit(flags) {
|
|
219
320
|
const noStratum = flags.includes('--no-stratum')
|
|
220
321
|
const noLifecycle = flags.includes('--no-lifecycle')
|
|
322
|
+
// init creates the workspace — never go through resolveCwdWithWorkspace (which
|
|
323
|
+
// requires one to exist). Strip --workspace if present to avoid leaving it in
|
|
324
|
+
// the shared args array for downstream subcommands.
|
|
325
|
+
getWorkspaceFlag(args)
|
|
221
326
|
const cwd = process.cwd()
|
|
222
327
|
|
|
223
328
|
// 1. Create .compose/ directory
|
|
@@ -434,6 +539,127 @@ function runSetup() {
|
|
|
434
539
|
}
|
|
435
540
|
}
|
|
436
541
|
|
|
542
|
+
// ---------------------------------------------------------------------------
|
|
543
|
+
// compose update — pull latest, reinstall deps, refresh global skill
|
|
544
|
+
// ---------------------------------------------------------------------------
|
|
545
|
+
|
|
546
|
+
function detectInstallStyle() {
|
|
547
|
+
// npm install resolves bin to either:
|
|
548
|
+
// - global: /<prefix>/lib/node_modules/@smartmemory/compose/bin/compose.js
|
|
549
|
+
// - local: <project>/node_modules/@smartmemory/compose/bin/compose.js
|
|
550
|
+
// - npx cache: ~/.npm/_npx/<hash>/node_modules/@smartmemory/compose/bin/compose.js
|
|
551
|
+
// git clone places it under any path WITHOUT a node_modules ancestor that
|
|
552
|
+
// contains the package itself, but WITH a .git directory at PACKAGE_ROOT.
|
|
553
|
+
if (PACKAGE_ROOT.includes(`${sep}node_modules${sep}`)) {
|
|
554
|
+
return { style: 'npm', root: PACKAGE_ROOT }
|
|
555
|
+
}
|
|
556
|
+
if (existsSync(join(PACKAGE_ROOT, '.git'))) {
|
|
557
|
+
return { style: 'git', root: PACKAGE_ROOT }
|
|
558
|
+
}
|
|
559
|
+
return { style: 'unknown', root: PACKAGE_ROOT }
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function getPkgVersion() {
|
|
563
|
+
try {
|
|
564
|
+
return JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf-8')).version
|
|
565
|
+
} catch { return 'unknown' }
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function getGitSha(repoPath) {
|
|
569
|
+
const r = spawnSync('git', ['-C', repoPath, 'rev-parse', '--short', 'HEAD'], { encoding: 'utf-8' })
|
|
570
|
+
return r.status === 0 ? r.stdout.trim() : null
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async function runUpdate(flags) {
|
|
574
|
+
const force = flags.includes('--force')
|
|
575
|
+
// update is user-global — workspace is optional. Try to resolve; if no
|
|
576
|
+
// workspace exists, just use process.cwd() (we may still operate on
|
|
577
|
+
// user-global state below).
|
|
578
|
+
const wsId = getWorkspaceFlag(args)
|
|
579
|
+
let cwd
|
|
580
|
+
try {
|
|
581
|
+
cwd = resolveWorkspace({ workspaceId: wsId }).root
|
|
582
|
+
_resolvedCwdCache = cwd
|
|
583
|
+
} catch (err) {
|
|
584
|
+
// Only WorkspaceUnset is benign for `update` (user-global). Any explicit
|
|
585
|
+
// mistake (bad --workspace, collision, ambiguity, too-broad) should still die.
|
|
586
|
+
if (err.code !== 'WorkspaceUnset') dieOnWorkspaceError(err)
|
|
587
|
+
cwd = process.cwd()
|
|
588
|
+
}
|
|
589
|
+
const { style, root } = detectInstallStyle()
|
|
590
|
+
|
|
591
|
+
console.log(`compose update — install style: ${style}`)
|
|
592
|
+
console.log(` root: ${root}`)
|
|
593
|
+
console.log(` current: v${getPkgVersion()}${style === 'git' ? ` @ ${getGitSha(root) || '?'}` : ''}`)
|
|
594
|
+
console.log('')
|
|
595
|
+
|
|
596
|
+
if (style === 'unknown') {
|
|
597
|
+
console.error('Cannot determine install style. Expected either:')
|
|
598
|
+
console.error(` - npm install: PACKAGE_ROOT inside node_modules`)
|
|
599
|
+
console.error(` - git clone: .git directory at ${root}`)
|
|
600
|
+
console.error('Reinstall with: npm install -g @smartmemory/compose')
|
|
601
|
+
process.exit(1)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (style === 'npm') {
|
|
605
|
+
// Decide global vs local: global if root is under a global prefix.
|
|
606
|
+
const npmPrefix = spawnSync('npm', ['prefix', '-g'], { encoding: 'utf-8' }).stdout.trim()
|
|
607
|
+
const isGlobal = npmPrefix && root.startsWith(npmPrefix)
|
|
608
|
+
const installCmd = isGlobal
|
|
609
|
+
? ['install', '-g', '@smartmemory/compose@latest']
|
|
610
|
+
: ['install', '@smartmemory/compose@latest']
|
|
611
|
+
console.log(`Running: npm ${installCmd.join(' ')}`)
|
|
612
|
+
const r = spawnSync('npm', installCmd, { stdio: 'inherit' })
|
|
613
|
+
if (r.status !== 0) {
|
|
614
|
+
console.error('npm install failed')
|
|
615
|
+
process.exit(r.status || 1)
|
|
616
|
+
}
|
|
617
|
+
} else {
|
|
618
|
+
// git clone — check clean, fast-forward pull, npm install
|
|
619
|
+
const status = spawnSync('git', ['-C', root, 'status', '--porcelain'], { encoding: 'utf-8' })
|
|
620
|
+
if (status.stdout.trim() && !force) {
|
|
621
|
+
console.error(`Working tree at ${root} has uncommitted changes.`)
|
|
622
|
+
console.error('Commit/stash them, or re-run with --force to skip the check.')
|
|
623
|
+
process.exit(1)
|
|
624
|
+
}
|
|
625
|
+
const beforeSha = getGitSha(root)
|
|
626
|
+
console.log(`Running: git fetch && git pull --ff-only`)
|
|
627
|
+
const fetch = spawnSync('git', ['-C', root, 'fetch'], { stdio: 'inherit' })
|
|
628
|
+
if (fetch.status !== 0) { process.exit(fetch.status || 1) }
|
|
629
|
+
const pull = spawnSync('git', ['-C', root, 'pull', '--ff-only'], { stdio: 'inherit' })
|
|
630
|
+
if (pull.status !== 0) {
|
|
631
|
+
console.error('git pull --ff-only failed (likely diverged from remote).')
|
|
632
|
+
console.error(`Reconcile manually in ${root}, then re-run compose update.`)
|
|
633
|
+
process.exit(pull.status || 1)
|
|
634
|
+
}
|
|
635
|
+
const afterSha = getGitSha(root)
|
|
636
|
+
if (beforeSha === afterSha) {
|
|
637
|
+
console.log(`Already up to date at ${afterSha}.`)
|
|
638
|
+
} else {
|
|
639
|
+
console.log(`Updated ${beforeSha} → ${afterSha}`)
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
console.log('Running: npm install')
|
|
643
|
+
const ni = spawnSync('npm', ['install'], { cwd: root, stdio: 'inherit' })
|
|
644
|
+
if (ni.status !== 0) { process.exit(ni.status || 1) }
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Refresh global skill + stratum-mcp registration
|
|
648
|
+
console.log('')
|
|
649
|
+
console.log('Refreshing global skill installation...')
|
|
650
|
+
runSetup()
|
|
651
|
+
|
|
652
|
+
// If invoked from inside a Compose project, refresh project artifacts too
|
|
653
|
+
if (existsSync(join(cwd, '.compose', 'compose.json'))) {
|
|
654
|
+
console.log('')
|
|
655
|
+
console.log(`Refreshing project at ${cwd}...`)
|
|
656
|
+
await runInit([])
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
console.log('')
|
|
660
|
+
console.log(`compose updated to v${getPkgVersion()}${style === 'git' ? ` @ ${getGitSha(root) || '?'}` : ''}`)
|
|
661
|
+
}
|
|
662
|
+
|
|
437
663
|
// ---------------------------------------------------------------------------
|
|
438
664
|
// Command dispatch
|
|
439
665
|
// ---------------------------------------------------------------------------
|
|
@@ -449,7 +675,12 @@ if (cmd === 'setup') {
|
|
|
449
675
|
}
|
|
450
676
|
|
|
451
677
|
if (cmd === 'doctor') {
|
|
452
|
-
runDoctor(args)
|
|
678
|
+
await runDoctor(args)
|
|
679
|
+
process.exit(0)
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (cmd === 'update' || cmd === 'upgrade') {
|
|
683
|
+
await runUpdate(args)
|
|
453
684
|
process.exit(0)
|
|
454
685
|
}
|
|
455
686
|
|
|
@@ -461,7 +692,7 @@ if (cmd === 'install') {
|
|
|
461
692
|
}
|
|
462
693
|
|
|
463
694
|
if (cmd === 'import') {
|
|
464
|
-
const cwd =
|
|
695
|
+
const cwd = resolveCwdWithWorkspace(args)
|
|
465
696
|
|
|
466
697
|
// Auto-init if needed
|
|
467
698
|
if (!existsSync(join(cwd, '.compose', 'compose.json'))) {
|
|
@@ -502,7 +733,7 @@ if (cmd === 'new') {
|
|
|
502
733
|
process.exit(1)
|
|
503
734
|
}
|
|
504
735
|
|
|
505
|
-
const cwd =
|
|
736
|
+
const cwd = resolveCwdWithWorkspace(args)
|
|
506
737
|
const name = basename(cwd)
|
|
507
738
|
|
|
508
739
|
// --from-idea <ID>: pre-populate intent from a promoted ideabox entry (Item 184)
|
|
@@ -630,7 +861,7 @@ if (cmd === 'feature') {
|
|
|
630
861
|
process.exit(1)
|
|
631
862
|
}
|
|
632
863
|
|
|
633
|
-
const cwd =
|
|
864
|
+
const cwd = resolveCwdWithWorkspace(args)
|
|
634
865
|
const configPath = join(cwd, '.compose', 'compose.json')
|
|
635
866
|
if (!existsSync(configPath)) {
|
|
636
867
|
console.error("No .compose/compose.json found. Run 'compose init' first.")
|
|
@@ -796,7 +1027,7 @@ if (cmd === 'roadmap') {
|
|
|
796
1027
|
// compose roadmap generate — regenerate ROADMAP.md from feature.json files
|
|
797
1028
|
if (subcmd === 'generate' || subcmd === 'gen') {
|
|
798
1029
|
const { writeRoadmap } = await import('../lib/roadmap-gen.js')
|
|
799
|
-
const cwd =
|
|
1030
|
+
const cwd = resolveCwdWithWorkspace(args)
|
|
800
1031
|
const path = writeRoadmap(cwd)
|
|
801
1032
|
console.log(`Generated ${path} from feature.json files`)
|
|
802
1033
|
process.exit(0)
|
|
@@ -805,7 +1036,7 @@ if (cmd === 'roadmap') {
|
|
|
805
1036
|
// compose roadmap migrate — extract ROADMAP.md entries into feature.json files
|
|
806
1037
|
if (subcmd === 'migrate') {
|
|
807
1038
|
const { migrateRoadmap } = await import('../lib/migrate-roadmap.js')
|
|
808
|
-
const cwd =
|
|
1039
|
+
const cwd = resolveCwdWithWorkspace(args)
|
|
809
1040
|
const dryRun = args.includes('--dry-run')
|
|
810
1041
|
const overwrite = args.includes('--overwrite')
|
|
811
1042
|
const result = migrateRoadmap(cwd, { dryRun, overwrite })
|
|
@@ -824,7 +1055,7 @@ if (cmd === 'roadmap') {
|
|
|
824
1055
|
if (subcmd === 'check') {
|
|
825
1056
|
const { listFeatures } = await import('../lib/feature-json.js')
|
|
826
1057
|
const { parseRoadmap } = await import('../lib/roadmap-parser.js')
|
|
827
|
-
const cwd =
|
|
1058
|
+
const cwd = resolveCwdWithWorkspace(args)
|
|
828
1059
|
const roadmapPath = join(cwd, 'ROADMAP.md')
|
|
829
1060
|
if (!existsSync(roadmapPath)) {
|
|
830
1061
|
console.error('No ROADMAP.md found. Run: compose roadmap generate')
|
|
@@ -945,7 +1176,7 @@ if (cmd === 'roadmap') {
|
|
|
945
1176
|
}
|
|
946
1177
|
}
|
|
947
1178
|
|
|
948
|
-
const cwd =
|
|
1179
|
+
const cwd = resolveCwdWithWorkspace(args)
|
|
949
1180
|
const roadmapPath = join(cwd, 'ROADMAP.md')
|
|
950
1181
|
|
|
951
1182
|
if (existsSync(roadmapPath)) {
|
|
@@ -1102,7 +1333,7 @@ if (cmd === 'record-completion') {
|
|
|
1102
1333
|
if (flags['force'] === true) completionArgs.force = true
|
|
1103
1334
|
if (flags['idempotency-key']) completionArgs.idempotency_key = flags['idempotency-key']
|
|
1104
1335
|
|
|
1105
|
-
const cwd =
|
|
1336
|
+
const cwd = resolveCwdWithWorkspace(args)
|
|
1106
1337
|
const { recordCompletion } = await import('../lib/completion-writer.js')
|
|
1107
1338
|
try {
|
|
1108
1339
|
const result = await recordCompletion(cwd, completionArgs)
|
|
@@ -1170,7 +1401,7 @@ if (cmd === 'hooks') {
|
|
|
1170
1401
|
const { join: pjoin, resolve: presolve } = await import('path')
|
|
1171
1402
|
const { fileURLToPath: futp } = await import('url')
|
|
1172
1403
|
|
|
1173
|
-
const projectRoot =
|
|
1404
|
+
const projectRoot = resolveCwdWithWorkspace(args)
|
|
1174
1405
|
const gitDir = pjoin(projectRoot, '.git')
|
|
1175
1406
|
if (!exSync(gitDir)) {
|
|
1176
1407
|
console.error('Error: not a git repository (no .git directory found)')
|
|
@@ -1225,14 +1456,24 @@ if (cmd === 'hooks') {
|
|
|
1225
1456
|
return 1
|
|
1226
1457
|
}
|
|
1227
1458
|
}
|
|
1459
|
+
// Hook install must pick exactly one workspace. Repo-level hooks bake one ID.
|
|
1460
|
+
const wsHint = hookFlags['workspace']
|
|
1461
|
+
let wsId
|
|
1462
|
+
try {
|
|
1463
|
+
wsId = resolveWorkspace({ cwd: projectRoot, workspaceId: wsHint }).id
|
|
1464
|
+
} catch (err) {
|
|
1465
|
+
dieOnWorkspaceError(err)
|
|
1466
|
+
}
|
|
1228
1467
|
const substituted = template
|
|
1229
1468
|
.replace(/__COMPOSE_NODE__/g, composeNode)
|
|
1230
1469
|
.replace(/__COMPOSE_BIN__/g, composeBin)
|
|
1470
|
+
.replace(/__COMPOSE_WORKSPACE_ID__/g, wsId)
|
|
1231
1471
|
wfSync(dest, substituted)
|
|
1232
1472
|
chmodSync(dest, 0o755)
|
|
1233
1473
|
console.log(`Installed ${type} hook at ${dest}`)
|
|
1234
1474
|
console.log(` COMPOSE_NODE=${composeNode}`)
|
|
1235
1475
|
console.log(` COMPOSE_BIN=${composeBin}`)
|
|
1476
|
+
console.log(` COMPOSE_WORKSPACE_ID=${wsId}`)
|
|
1236
1477
|
return 0
|
|
1237
1478
|
}
|
|
1238
1479
|
|
|
@@ -1253,6 +1494,11 @@ if (cmd === 'hooks') {
|
|
|
1253
1494
|
return 0
|
|
1254
1495
|
}
|
|
1255
1496
|
|
|
1497
|
+
function extractBakedWorkspaceId(content) {
|
|
1498
|
+
const m = content.match(/^COMPOSE_WORKSPACE_ID="([^"]*)"$/m)
|
|
1499
|
+
return m ? m[1] : null
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1256
1502
|
function statusOne(type) {
|
|
1257
1503
|
const { marker, dest } = HOOK_TYPES[type]
|
|
1258
1504
|
if (!exSync(dest)) {
|
|
@@ -1264,12 +1510,29 @@ if (cmd === 'hooks') {
|
|
|
1264
1510
|
console.log(`${type}: foreign — hook exists but is not a Compose hook`)
|
|
1265
1511
|
return
|
|
1266
1512
|
}
|
|
1513
|
+
const wsHint = hookFlags['workspace']
|
|
1514
|
+
let expectedWsId = null
|
|
1515
|
+
if (wsHint) {
|
|
1516
|
+
try { expectedWsId = resolveWorkspace({ cwd: projectRoot, workspaceId: wsHint }).id } catch { /* ignore for status */ }
|
|
1517
|
+
} else {
|
|
1518
|
+
try { expectedWsId = resolveWorkspace({ cwd: projectRoot }).id } catch { /* ignore for status */ }
|
|
1519
|
+
}
|
|
1267
1520
|
const nodeMatch = content.includes(`COMPOSE_NODE="${composeNode}"`)
|
|
1268
1521
|
const binMatch = content.includes(`COMPOSE_BIN="${composeBin}"`)
|
|
1269
|
-
|
|
1522
|
+
const hasRawToken = content.includes('__COMPOSE_WORKSPACE_ID__')
|
|
1523
|
+
const wsMatch = hasRawToken ? false
|
|
1524
|
+
: expectedWsId ? content.includes(`COMPOSE_WORKSPACE_ID="${expectedWsId}"`)
|
|
1525
|
+
: true
|
|
1526
|
+
if (nodeMatch && binMatch && wsMatch && !hasRawToken) {
|
|
1270
1527
|
console.log(`${type}: installed (current)`)
|
|
1528
|
+
const baked = extractBakedWorkspaceId(content)
|
|
1529
|
+
if (baked) console.log(` workspace: ${baked}`)
|
|
1271
1530
|
} else {
|
|
1272
|
-
|
|
1531
|
+
const reason = hasRawToken ? 'MISSING_WORKSPACE_ID'
|
|
1532
|
+
: (expectedWsId && !wsMatch) ? 'STALE_WORKSPACE_ID'
|
|
1533
|
+
: 'stale paths'
|
|
1534
|
+
console.log(`${type}: installed (${reason} — re-run install)`)
|
|
1535
|
+
if (expectedWsId && !wsMatch && !hasRawToken) console.log(` expected COMPOSE_WORKSPACE_ID="${expectedWsId}"`)
|
|
1273
1536
|
if (!nodeMatch) console.log(` expected COMPOSE_NODE="${composeNode}"`)
|
|
1274
1537
|
if (!binMatch) console.log(` expected COMPOSE_BIN="${composeBin}"`)
|
|
1275
1538
|
}
|
|
@@ -1360,11 +1623,12 @@ Exit codes:
|
|
|
1360
1623
|
}
|
|
1361
1624
|
|
|
1362
1625
|
const { validateFeature, validateProject } = await import('../lib/feature-validator.js')
|
|
1626
|
+
const valCwd = resolveCwdWithWorkspace(args)
|
|
1363
1627
|
let result
|
|
1364
1628
|
try {
|
|
1365
1629
|
result = scope === 'feature'
|
|
1366
|
-
? await validateFeature(
|
|
1367
|
-
: await validateProject(
|
|
1630
|
+
? await validateFeature(valCwd, code)
|
|
1631
|
+
: await validateProject(valCwd)
|
|
1368
1632
|
} catch (err) {
|
|
1369
1633
|
if (err.code === 'INVALID_INPUT') {
|
|
1370
1634
|
console.error(`Error [INVALID_INPUT]: ${err.message}`)
|
|
@@ -1406,8 +1670,9 @@ Exit codes:
|
|
|
1406
1670
|
|
|
1407
1671
|
if (cmd === 'pipeline') {
|
|
1408
1672
|
const { runPipelineCli } = await import('../lib/pipeline-cli.js')
|
|
1673
|
+
const pipeCwd = resolveCwdWithWorkspace(args)
|
|
1409
1674
|
try {
|
|
1410
|
-
runPipelineCli(
|
|
1675
|
+
runPipelineCli(pipeCwd, args)
|
|
1411
1676
|
} catch (err) {
|
|
1412
1677
|
console.error(`Error: ${err.message}`)
|
|
1413
1678
|
process.exit(1)
|
|
@@ -1488,7 +1753,7 @@ if (cmd === 'build') {
|
|
|
1488
1753
|
}
|
|
1489
1754
|
|
|
1490
1755
|
// Auto-init if needed
|
|
1491
|
-
const buildCwd =
|
|
1756
|
+
const buildCwd = resolveCwdWithWorkspace(args)
|
|
1492
1757
|
if (!existsSync(join(buildCwd, '.compose', 'compose.json')) || !existsSync(join(buildCwd, 'pipelines', 'build.stratum.yaml'))) {
|
|
1493
1758
|
console.log('Running compose init...\n')
|
|
1494
1759
|
await runInit(args.filter(a => a.startsWith('--')))
|
|
@@ -1563,7 +1828,7 @@ if (cmd === 'build') {
|
|
|
1563
1828
|
process.exit(1)
|
|
1564
1829
|
}
|
|
1565
1830
|
|
|
1566
|
-
const fixCwd =
|
|
1831
|
+
const fixCwd = resolveCwdWithWorkspace(args)
|
|
1567
1832
|
if (!existsSync(join(fixCwd, '.compose', 'compose.json')) || !existsSync(join(fixCwd, 'pipelines', 'bug-fix.stratum.yaml'))) {
|
|
1568
1833
|
console.log('Running compose init...\n')
|
|
1569
1834
|
await runInit(args.filter(a => a.startsWith('--')))
|
|
@@ -1650,7 +1915,7 @@ if (cmd === 'build') {
|
|
|
1650
1915
|
}
|
|
1651
1916
|
import('../lib/triage.js').then(({ runTriage }) => {
|
|
1652
1917
|
import('../lib/feature-json.js').then(({ readFeature, writeFeature, updateFeature }) => {
|
|
1653
|
-
const trCwd =
|
|
1918
|
+
const trCwd = resolveCwdWithWorkspace(args)
|
|
1654
1919
|
runTriage(triageCode, { cwd: trCwd }).then((result) => {
|
|
1655
1920
|
console.log(`\nFeature: ${triageCode}`)
|
|
1656
1921
|
console.log(`Tier: ${result.tier}`)
|
|
@@ -1694,16 +1959,11 @@ if (cmd === 'build') {
|
|
|
1694
1959
|
})
|
|
1695
1960
|
})
|
|
1696
1961
|
} else if (cmd === 'start') {
|
|
1697
|
-
// Resolve target root BEFORE spawning supervisor
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
if (explicitTarget && !existsSync(resolve(explicitTarget))) {
|
|
1704
|
-
console.error(`[compose] COMPOSE_TARGET=${explicitTarget} does not exist.`)
|
|
1705
|
-
process.exit(1)
|
|
1706
|
-
}
|
|
1962
|
+
// Resolve target root BEFORE spawning supervisor.
|
|
1963
|
+
// Use the unified resolver — it handles COMPOSE_TARGET as either ID or absolute path,
|
|
1964
|
+
// --workspace=<id>, and discovery. No need for the legacy explicitTarget short-circuit.
|
|
1965
|
+
const targetRoot = resolveCwdWithWorkspace(args)
|
|
1966
|
+
|
|
1707
1967
|
if (!targetRoot || !existsSync(join(targetRoot, '.compose', 'compose.json'))) {
|
|
1708
1968
|
console.error('[compose] No .compose/ found (searched from cwd upward).')
|
|
1709
1969
|
console.error("[compose] Run 'compose init' first, or set COMPOSE_TARGET.")
|
|
@@ -1725,7 +1985,7 @@ if (cmd === 'build') {
|
|
|
1725
1985
|
// compose ideabox — idea management CLI
|
|
1726
1986
|
// ---------------------------------------------------------------------------
|
|
1727
1987
|
const ibSubcmd = args[0]
|
|
1728
|
-
const ibCwd =
|
|
1988
|
+
const ibCwd = resolveCwdWithWorkspace(args)
|
|
1729
1989
|
|
|
1730
1990
|
// Resolve compose config (paths, etc.)
|
|
1731
1991
|
function loadComposeConfig(cwd) {
|
|
@@ -2024,7 +2284,7 @@ if (cmd === 'build') {
|
|
|
2024
2284
|
process.exit(1)
|
|
2025
2285
|
}
|
|
2026
2286
|
|
|
2027
|
-
const qsCwd =
|
|
2287
|
+
const qsCwd = resolveCwdWithWorkspace(args)
|
|
2028
2288
|
|
|
2029
2289
|
import('../lib/feature-json.js').then(({ readFeature }) => {
|
|
2030
2290
|
import('../lib/qa-scoping.js').then(({ mapFilesToRoutes, classifyRoutes }) => {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
set -u
|
|
7
7
|
COMPOSE_NODE="__COMPOSE_NODE__"
|
|
8
8
|
COMPOSE_BIN="__COMPOSE_BIN__"
|
|
9
|
+
COMPOSE_WORKSPACE_ID="__COMPOSE_WORKSPACE_ID__"
|
|
9
10
|
LOG="${COMPOSE_HOOK_LOG:-.compose/data/post-commit.log}"
|
|
10
11
|
mkdir -p "$(dirname "$LOG")"
|
|
11
12
|
|
|
@@ -52,7 +53,7 @@ echo "$trailers" | while IFS= read -r line; do
|
|
|
52
53
|
done
|
|
53
54
|
|
|
54
55
|
if ! echo "$files" | "$COMPOSE_NODE" "$COMPOSE_BIN" record-completion "$code" \
|
|
55
|
-
--commit-sha="$sha" --tests-pass="$tp" --notes="$notes" \
|
|
56
|
+
--commit-sha="$sha" --tests-pass="$tp" --notes="$notes" --workspace="$COMPOSE_WORKSPACE_ID" \
|
|
56
57
|
--files-changed-from-stdin >> "$LOG" 2>&1; then
|
|
57
58
|
echo "[$(date -Iseconds)] hook: record_completion failed for $code (sha=$sha) — see above" >> "$LOG"
|
|
58
59
|
fi
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
set -u
|
|
7
7
|
COMPOSE_NODE="__COMPOSE_NODE__"
|
|
8
8
|
COMPOSE_BIN="__COMPOSE_BIN__"
|
|
9
|
+
COMPOSE_WORKSPACE_ID="__COMPOSE_WORKSPACE_ID__"
|
|
9
10
|
LOG="${COMPOSE_HOOK_LOG:-.compose/data/pre-push.log}"
|
|
10
11
|
mkdir -p "$(dirname "$LOG")" 2>/dev/null || true
|
|
11
12
|
|
|
12
|
-
OUTPUT=$("$COMPOSE_NODE" "$COMPOSE_BIN" validate --scope=project --block-on=error 2>&1)
|
|
13
|
+
OUTPUT=$("$COMPOSE_NODE" "$COMPOSE_BIN" validate --scope=project --block-on=error --workspace="$COMPOSE_WORKSPACE_ID" 2>&1)
|
|
13
14
|
EXIT_CODE=$?
|
|
14
15
|
|
|
15
16
|
if [ "$EXIT_CODE" -ne 0 ]; then
|