@smartmemory/compose 0.5.0 → 0.5.1
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 +14 -0
- package/bin/compose.js +24 -3
- package/lib/agent-string.js +9 -4
- package/lib/build-stream-writer.js +6 -0
- package/lib/build.js +643 -84
- package/lib/consumer-fanout.js +403 -16
- package/lib/experiment-pricing.js +5 -1
- package/lib/flow-state.js +38 -0
- package/lib/gsd.js +95 -48
- package/lib/model-pricing.js +4 -1
- package/lib/output-gate.js +81 -0
- package/lib/pipeline-profiles.js +200 -0
- package/lib/result-normalizer.js +13 -0
- package/lib/stratum-mcp-client.js +4 -4
- package/lib/team-flag.js +1 -1
- package/lib/wave-checkpoint.js +100 -0
- package/package.json +2 -2
- package/presets/team-fable-astra.profiles.json +18 -0
- package/presets/team-fable-astra.stratum.yaml +236 -0
- package/server/model-tiers.js +14 -6
package/README.md
CHANGED
|
@@ -74,6 +74,20 @@ compose plan "a tool that summarizes my team's standups"
|
|
|
74
74
|
|
|
75
75
|
`compose build` then picks up a plan-authored feature and ratifies its design rather than rewriting it.
|
|
76
76
|
|
|
77
|
+
Bundled [team presets](docs/team-presets.md): `feature` (parallel implementation),
|
|
78
|
+
`research` (parallel exploration), `review` (parallel review), and `fable-astra`:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
compose build FEAT-1 --team fable-astra
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Fable plans independent tasks, Codex workers implement in isolated worktrees, and
|
|
85
|
+
a fresh read-only Astra reviewer checks the merged result after verification.
|
|
86
|
+
Fable then requests another implementation or repair wave, declares the work
|
|
87
|
+
blocked, or approves ship into one base-parent commit. Concurrency is 3; the
|
|
88
|
+
$150 cost ceiling is overridable with `--cost-ceiling-usd`. See the
|
|
89
|
+
[loop and limits](docs/pipelines.md#fable-astra-wave-loop).
|
|
90
|
+
|
|
77
91
|
## Quick install
|
|
78
92
|
|
|
79
93
|
Prerequisites: Node.js 18+. [Stratum](https://github.com/smartmemory/stratum) needs no separate install — `@smartmemory/stratum` is a dependency, and `compose init` registers the installed copy's MCP entrypoint automatically (a sibling `stratum/` checkout is a development convenience, not a requirement; the python `stratum-mcp` PyPI package is retired). Codex steps additionally need the OpenAI `codex` CLI. Full prereqs in [docs/install.md](docs/install.md).
|
package/bin/compose.js
CHANGED
|
@@ -567,8 +567,9 @@ async function runInit(flags, cwdOverride) {
|
|
|
567
567
|
// and then fails with "Lifecycle spec not found" because init never copied it —
|
|
568
568
|
// so the command was unavailable in every fresh workspace independently of the
|
|
569
569
|
// spec's dialect. Each spec's `<name>.profiles.json` sidecar travels WITH it:
|
|
570
|
-
// loadPipelineProfiles
|
|
571
|
-
//
|
|
570
|
+
// loadPipelineProfiles treats a missing string-only sidecar as bare defaults (the
|
|
571
|
+
// tool restrictions it declares silently vanish), and runBuild REFUSES a local copy
|
|
572
|
+
// of a preset whose sidecar configures execution (team-fable-astra) without it.
|
|
572
573
|
const pipelinesDir = join(cwd, 'pipelines')
|
|
573
574
|
mkdirSync(pipelinesDir, { recursive: true })
|
|
574
575
|
const DEFAULT_PIPELINES = [
|
|
@@ -2619,6 +2620,20 @@ if (cmd === 'build') {
|
|
|
2619
2620
|
}
|
|
2620
2621
|
let filteredArgs = args.filter((a, i) => i !== cwdIdx && (cwdIdx === -1 || i !== cwdIdx + 1))
|
|
2621
2622
|
|
|
2623
|
+
// Extract value-taking build flags before --team counts positional features.
|
|
2624
|
+
// --cwd is already removed; leave --template for the explicit team conflict.
|
|
2625
|
+
const ceilingIdx = filteredArgs.findIndex(a => a === '--cost-ceiling-usd' || a.startsWith('--cost-ceiling-usd='))
|
|
2626
|
+
let costCeilingUsd
|
|
2627
|
+
if (ceilingIdx !== -1) {
|
|
2628
|
+
const inline = filteredArgs[ceilingIdx].includes('=')
|
|
2629
|
+
costCeilingUsd = Number(inline ? filteredArgs[ceilingIdx].split('=')[1] : filteredArgs[ceilingIdx + 1])
|
|
2630
|
+
if (!Number.isFinite(costCeilingUsd) || costCeilingUsd <= 0) {
|
|
2631
|
+
console.error('--cost-ceiling-usd requires a finite positive USD amount')
|
|
2632
|
+
process.exit(1)
|
|
2633
|
+
}
|
|
2634
|
+
filteredArgs.splice(ceilingIdx, inline ? 1 : 2)
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2622
2637
|
// --team flag (COMP-TEAMS)
|
|
2623
2638
|
let teamTemplate = null
|
|
2624
2639
|
try {
|
|
@@ -2667,7 +2682,7 @@ if (cmd === 'build') {
|
|
|
2667
2682
|
if (teamTemplate && !templateName) {
|
|
2668
2683
|
templateName = teamTemplate
|
|
2669
2684
|
}
|
|
2670
|
-
|
|
2685
|
+
let filteredArgs2 = filteredArgs.filter((a, i) => i !== templateIdx && (templateIdx === -1 || i !== templateIdx + 1))
|
|
2671
2686
|
|
|
2672
2687
|
const featureCodes = filteredArgs2.filter(a => !a.startsWith('-'))
|
|
2673
2688
|
const featureCode = featureCodes[0]
|
|
@@ -2725,6 +2740,10 @@ if (cmd === 'build') {
|
|
|
2725
2740
|
console.error('Error: --abort and --all/prefix/multi are mutually exclusive')
|
|
2726
2741
|
process.exit(1)
|
|
2727
2742
|
}
|
|
2743
|
+
if (costCeilingUsd !== undefined && isBatch) {
|
|
2744
|
+
console.error('--cost-ceiling-usd is single-build only; batch is not supported')
|
|
2745
|
+
process.exit(1)
|
|
2746
|
+
}
|
|
2728
2747
|
if (resume && fresh) {
|
|
2729
2748
|
console.error('--resume and --fresh are mutually exclusive')
|
|
2730
2749
|
process.exit(1)
|
|
@@ -2780,6 +2799,7 @@ if (cmd === 'build') {
|
|
|
2780
2799
|
console.error(' --abort Abort the active build')
|
|
2781
2800
|
console.error(' --resume Resume the active build for <feature-code>')
|
|
2782
2801
|
console.error(' --fresh Start a fresh build, discarding stale failed/resumable state')
|
|
2802
|
+
console.error(' --cost-ceiling-usd <amount> Override a configured ceiling (single build; holds still require a human)')
|
|
2783
2803
|
console.error(' --all Build all PLANNED features in dependency order')
|
|
2784
2804
|
console.error(' --dry-run Print build order without executing')
|
|
2785
2805
|
console.error(' --cwd <path> Agent working directory (for cross-repo features)')
|
|
@@ -2856,6 +2876,7 @@ if (cmd === 'build') {
|
|
|
2856
2876
|
if (implementerArg) singleOpts.implementer = implementerArg // COMP-MODEL-AB
|
|
2857
2877
|
if (reviewerArg) singleOpts.reviewer = reviewerArg // COMP-MODEL-AB
|
|
2858
2878
|
if (resume) singleOpts.resume = true
|
|
2879
|
+
if (costCeilingUsd !== undefined) singleOpts.costCeilingUsd = costCeilingUsd
|
|
2859
2880
|
if (fresh) singleOpts.fresh = true
|
|
2860
2881
|
if (nonInteractiveBuild) singleOpts.gateOpts = { nonInteractive: true }
|
|
2861
2882
|
if (resumeFlowId) singleOpts.resumeFlowId = resumeFlowId
|
package/lib/agent-string.js
CHANGED
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
* "claude:read-only-reviewer:fast" → provider + template + tier
|
|
9
9
|
* "claude::fast" → provider + tier (no template)
|
|
10
10
|
*
|
|
11
|
-
* Tiers: critical | standard | fast (maps to Opus / Sonnet / Haiku via model-tiers.js)
|
|
11
|
+
* Tiers: critical | standard | fast | coordinator (maps to Opus / Sonnet / Haiku / Fable via model-tiers.js)
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { resolveTemplate } from '../server/agent-templates.js';
|
|
15
|
-
import { resolveTierModel, resolveTierThinking } from '../server/model-tiers.js';
|
|
15
|
+
import { MODEL_TIERS, resolveTierModel, resolveTierThinking } from '../server/model-tiers.js';
|
|
16
16
|
|
|
17
17
|
/** Known provider names — validated by validateAgentString. */
|
|
18
18
|
const KNOWN_PROVIDERS = new Set(['claude', 'codex']);
|
|
19
19
|
|
|
20
20
|
/** Known tier names — validated by validateAgentString (null = no tier = ok). */
|
|
21
|
-
const KNOWN_TIERS = new Set(
|
|
21
|
+
const KNOWN_TIERS = new Set(Object.keys(MODEL_TIERS));
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Parse a raw agent string into provider, template, and tier parts.
|
|
@@ -52,7 +52,7 @@ export function parseAgentString(raw) {
|
|
|
52
52
|
*
|
|
53
53
|
* Checks:
|
|
54
54
|
* - provider must be a known connector type (claude | codex)
|
|
55
|
-
* - tier, if present, must be a known tier (critical | standard | fast)
|
|
55
|
+
* - tier, if present, must be a known tier (critical | standard | fast | coordinator)
|
|
56
56
|
*
|
|
57
57
|
* COMP-MODEL-AB: used by --implementer / --reviewer flag validation.
|
|
58
58
|
*
|
|
@@ -73,6 +73,11 @@ export function validateAgentString(raw) {
|
|
|
73
73
|
`(known: ${[...KNOWN_TIERS].sort().join(', ')})`
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
|
+
if (tier != null && resolveTierModel(tier, provider) === null) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Invalid agent string "${raw}": tier "${tier}" is not available for provider "${provider}"`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
/**
|
|
@@ -198,6 +198,12 @@ export class BuildStreamWriter {
|
|
|
198
198
|
* @param {string} [status='complete'] Build exit status
|
|
199
199
|
* @param {object} [costTotals] Optional cumulative cost/token totals
|
|
200
200
|
*/
|
|
201
|
+
pause(detail = {}) {
|
|
202
|
+
if (this.#closed) return;
|
|
203
|
+
this.write({ type: 'build_paused', featureCode: this.#featureCode, ...detail });
|
|
204
|
+
this.#closed = true;
|
|
205
|
+
}
|
|
206
|
+
|
|
201
207
|
close(status = 'complete', costTotals = null) {
|
|
202
208
|
if (this.#closed) return;
|
|
203
209
|
this.#closed = true;
|