@openlife/cli 1.8.3 → 1.10.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.
- package/.catalog/agents/test-agent/AGENT.md +1 -1
- package/.catalog/mcps/test-mcp/mcp.json +1 -1
- package/.catalog/skills/sample-from-url/IMPORTED_REFERENCE.md +2 -2
- package/.catalog/skills/test-skill/REFERENCE.md +1 -1
- package/.catalog/squads/00-governance-advisory-board/workflows/wf-board-meeting.yaml +25 -0
- package/.catalog/squads/00-governance-advisory-board/workflows/wf-decision-framework.yaml +25 -0
- package/.catalog/squads/00-governance-c-level-squad/workflows/wf-board-presentation.yaml +25 -0
- package/.catalog/squads/00-governance-c-level-squad/workflows/wf-strategic-planning.yaml +25 -0
- package/.catalog/squads/00-governance-project-ops-squad/workflows/mission-to-project.yaml +25 -0
- package/.catalog/squads/00-governance-project-ops-squad/workflows/sprint-planning.yaml +25 -0
- package/.catalog/squads/00-governance-project-ops-squad/workflows/weekly-review.yaml +25 -0
- package/.catalog/squads/01-meta-framework-swarm-tree-orchestration/workflows/tree-analysis-flow.yaml +25 -0
- package/.catalog/squads/01-meta-framework-swarm-tree-orchestration/workflows/tree-execution-flow.yaml +25 -0
- package/.catalog/squads/01-meta-framework-swarm-tree-orchestration/workflows/tree-planning-flow.yaml +25 -0
- package/.catalog/squads/01-meta-framework-swarm-tree-orchestration/workflows/tree-resume-flow.yaml +25 -0
- package/.catalog/squads/02-product-growth-brand-squad/workflows/wf-brand-creation.yaml +25 -0
- package/.catalog/squads/02-product-growth-brand-squad/workflows/wf-rebrand.yaml +25 -0
- package/.catalog/squads/02-product-growth-copy-squad/workflows/wf-copy-review-cycle.yaml +25 -0
- package/.catalog/squads/02-product-growth-copy-squad/workflows/wf-full-copy-project.yaml +25 -0
- package/.catalog/squads/02-product-growth-design-squad/workflows/wf-design-system-creation.yaml +25 -0
- package/.catalog/squads/02-product-growth-design-squad/workflows/wf-feature-design.yaml +25 -0
- package/.catalog/squads/02-product-growth-hormozi-squad/workflows/wf-business-turnaround.yaml +25 -0
- package/.catalog/squads/02-product-growth-hormozi-squad/workflows/wf-offer-creation.yaml +25 -0
- package/.catalog/squads/02-product-growth-movement/workflows/wf-movement-launch.yaml +25 -0
- package/.catalog/squads/02-product-growth-saas-onboarding-activator/workflows/full-onboarding-activation-workflow.yaml +25 -0
- package/.catalog/squads/02-product-growth-saas-onboarding-activator/workflows/quick-engagement-boost-workflow.yaml +25 -0
- package/.catalog/squads/02-product-growth-sales-squad/workflows/followup-sequence.yaml +25 -0
- package/.catalog/squads/02-product-growth-sales-squad/workflows/full-sales-cycle.yaml +25 -0
- package/.catalog/squads/02-product-growth-sales-squad/workflows/proposal-flow.yaml +25 -0
- package/.catalog/squads/02-product-growth-storytelling/workflows/wf-brand-narrative.yaml +25 -0
- package/.catalog/squads/02-product-growth-storytelling/workflows/wf-story-development.yaml +25 -0
- package/.catalog/squads/02-product-growth-traffic-masters/workflows/wf-account-audit.yaml +25 -0
- package/.catalog/squads/02-product-growth-traffic-masters/workflows/wf-campaign-launch.yaml +25 -0
- package/.catalog/squads/03-engineering-quality-automated-code-review-squad/workflows/full-code-review-workflow.yaml +25 -0
- package/.catalog/squads/03-engineering-quality-automated-code-review-squad/workflows/quick-security-check-workflow.yaml +25 -0
- package/.catalog/squads/04-data-security-cybersecurity/workflows/wf-incident-response.yaml +25 -0
- package/.catalog/squads/04-data-security-cybersecurity/workflows/wf-pentest-engagement.yaml +25 -0
- package/.catalog/squads/04-data-security-data-quality-guardian/workflows/full-data-quality-audit-workflow.yaml +25 -0
- package/.catalog/squads/04-data-security-data-quality-guardian/workflows/quick-data-check-workflow.yaml +25 -0
- package/.catalog/squads/04-data-security-data-squad/workflows/wf-analytics-setup.yaml +25 -0
- package/.catalog/squads/04-data-security-data-squad/workflows/wf-growth-sprint.yaml +25 -0
- package/.catalog/squads/04-data-security-incident-response-squad/workflows/full-incident-response-workflow.yaml +25 -0
- package/.catalog/squads/04-data-security-incident-response-squad/workflows/rapid-triage-workflow.yaml +25 -0
- package/.catalog/squads/04-data-security-soc-alert-triage/workflows/full-alert-triage-workflow.yaml +25 -0
- package/.catalog/squads/04-data-security-soc-alert-triage/workflows/rapid-classification-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-adaptive-tutor-k12/workflows/full-tutoring-cycle-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-adaptive-tutor-k12/workflows/quick-practice-session-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-ambient-clinical-scribe/workflows/full-documentation-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-ambient-clinical-scribe/workflows/quick-note-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-contract-review-squad/workflows/full-contract-review-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-contract-review-squad/workflows/quick-risk-assessment-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-crypto-token-forge/workflows/full-token-launch-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-crypto-token-forge/workflows/memecoin-express-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-crypto-token-forge/workflows/utility-token-launch-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-resume-screener-squad/workflows/full-resume-screening-workflow.yaml +25 -0
- package/.catalog/squads/05-domain-specialists-resume-screener-squad/workflows/quick-skills-match-workflow.yaml +25 -0
- package/.catalog/squads/test-squad/SQUAD.md +1 -1
- package/.openlife/method/agents/atlas.md +102 -0
- package/.openlife/method/agents/builder.md +92 -0
- package/.openlife/method/agents/conductor.md +93 -0
- package/.openlife/method/agents/forge.md +96 -0
- package/.openlife/method/agents/genesis.md +115 -0
- package/.openlife/method/agents/lyra.md +85 -0
- package/.openlife/method/agents/maestro.md +92 -0
- package/.openlife/method/agents/mesh.md +101 -0
- package/.openlife/method/agents/prism.md +85 -0
- package/.openlife/method/agents/sentinel.md +115 -0
- package/.openlife/method/agents/steward.md +93 -0
- package/.openlife/method/agents/vortex.md +94 -0
- package/dist/cli/CommandFlowRunner.js +167 -0
- package/dist/cli/install/Phases.js +43 -0
- package/dist/index.js +118 -0
- package/dist/orchestrator/ProjectMetadata.js +183 -0
- package/dist/test_flow_run_cli.js +183 -0
- package/dist/test_host_uninstaller.js +12 -2
- package/dist/test_openlife_method_inventory.js +211 -0
- package/dist/test_workflow_e2e.js +10 -3
- package/dist-templates/claude-code/agents/openlife-atlas.md +12 -44
- package/dist-templates/claude-code/agents/openlife-builder.md +20 -0
- package/dist-templates/claude-code/agents/openlife-conductor.md +20 -0
- package/dist-templates/claude-code/agents/openlife-forge.md +12 -34
- package/dist-templates/claude-code/agents/openlife-genesis.md +12 -51
- package/dist-templates/claude-code/agents/openlife-lyra.md +12 -32
- package/dist-templates/claude-code/agents/openlife-maestro.md +27 -41
- package/dist-templates/claude-code/agents/openlife-mesh.md +20 -0
- package/dist-templates/claude-code/agents/openlife-prism.md +20 -0
- package/dist-templates/claude-code/agents/openlife-sentinel.md +20 -0
- package/dist-templates/claude-code/agents/openlife-steward.md +20 -0
- package/dist-templates/claude-code/agents/openlife-vortex.md +20 -0
- package/dist-templates/claude-code/commands/openlife/agents/atlas.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/builder.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/conductor.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/forge.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/genesis.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/lyra.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/maestro.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/mesh.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/prism.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/sentinel.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/steward.md +28 -0
- package/dist-templates/claude-code/commands/openlife/agents/vortex.md +28 -0
- package/dist-templates/claude-code/commands/openlife/ask.md +32 -9
- package/dist-templates/claude-code/commands/openlife/audit.md +43 -0
- package/dist-templates/claude-code/commands/openlife/doctor.md +9 -14
- package/dist-templates/claude-code/commands/openlife/dream.md +28 -13
- package/dist-templates/claude-code/commands/openlife/explore.md +32 -0
- package/dist-templates/claude-code/commands/openlife/flow/brownfield-discovery.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/brownfield-fullstack.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/brownfield-service.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/brownfield-ui.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/epic.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/greenfield-fullstack.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/greenfield-service.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/greenfield-ui.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/qa-loop.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/release.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/spec-pipeline.md +38 -0
- package/dist-templates/claude-code/commands/openlife/flow/story-cycle.md +38 -0
- package/dist-templates/claude-code/commands/openlife/health.md +37 -0
- package/dist-templates/claude-code/commands/openlife/plan.md +38 -0
- package/dist-templates/claude-code/commands/openlife/review.md +34 -0
- package/dist-templates/claude-code/commands/openlife/ship.md +35 -0
- package/dist-templates/claude-code/commands/openlife/start.md +38 -0
- package/dist-templates/claude-code/commands/openlife/status.md +8 -10
- package/dist-templates/claude-code/commands/openlife/story.md +34 -0
- package/dist-templates/codex/agents/openlife-atlas.md +12 -44
- package/dist-templates/codex/agents/openlife-builder.md +20 -0
- package/dist-templates/codex/agents/openlife-conductor.md +20 -0
- package/dist-templates/codex/agents/openlife-forge.md +12 -34
- package/dist-templates/codex/agents/openlife-genesis.md +12 -51
- package/dist-templates/codex/agents/openlife-lyra.md +12 -32
- package/dist-templates/codex/agents/openlife-maestro.md +27 -41
- package/dist-templates/codex/agents/openlife-mesh.md +20 -0
- package/dist-templates/codex/agents/openlife-prism.md +20 -0
- package/dist-templates/codex/agents/openlife-sentinel.md +20 -0
- package/dist-templates/codex/agents/openlife-steward.md +20 -0
- package/dist-templates/codex/agents/openlife-vortex.md +20 -0
- package/dist-templates/codex/commands/openlife/agents/atlas.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/builder.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/conductor.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/forge.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/genesis.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/lyra.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/maestro.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/mesh.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/prism.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/sentinel.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/steward.md +28 -0
- package/dist-templates/codex/commands/openlife/agents/vortex.md +28 -0
- package/dist-templates/codex/commands/openlife/ask.md +32 -9
- package/dist-templates/codex/commands/openlife/audit.md +43 -0
- package/dist-templates/codex/commands/openlife/doctor.md +9 -14
- package/dist-templates/codex/commands/openlife/dream.md +28 -13
- package/dist-templates/codex/commands/openlife/explore.md +32 -0
- package/dist-templates/codex/commands/openlife/flow/brownfield-discovery.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/brownfield-fullstack.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/brownfield-service.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/brownfield-ui.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/epic.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/greenfield-fullstack.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/greenfield-service.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/greenfield-ui.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/qa-loop.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/release.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/spec-pipeline.md +38 -0
- package/dist-templates/codex/commands/openlife/flow/story-cycle.md +38 -0
- package/dist-templates/codex/commands/openlife/health.md +37 -0
- package/dist-templates/codex/commands/openlife/plan.md +38 -0
- package/dist-templates/codex/commands/openlife/review.md +34 -0
- package/dist-templates/codex/commands/openlife/ship.md +35 -0
- package/dist-templates/codex/commands/openlife/start.md +38 -0
- package/dist-templates/codex/commands/openlife/status.md +8 -10
- package/dist-templates/codex/commands/openlife/story.md +34 -0
- package/dist-templates/gemini-cli/agents/openlife-atlas.md +12 -44
- package/dist-templates/gemini-cli/agents/openlife-builder.md +20 -0
- package/dist-templates/gemini-cli/agents/openlife-conductor.md +20 -0
- package/dist-templates/gemini-cli/agents/openlife-forge.md +12 -34
- package/dist-templates/gemini-cli/agents/openlife-genesis.md +12 -51
- package/dist-templates/gemini-cli/agents/openlife-lyra.md +12 -32
- package/dist-templates/gemini-cli/agents/openlife-maestro.md +27 -41
- package/dist-templates/gemini-cli/agents/openlife-mesh.md +20 -0
- package/dist-templates/gemini-cli/agents/openlife-prism.md +20 -0
- package/dist-templates/gemini-cli/agents/openlife-sentinel.md +20 -0
- package/dist-templates/gemini-cli/agents/openlife-steward.md +20 -0
- package/dist-templates/gemini-cli/agents/openlife-vortex.md +20 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/atlas.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/builder.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/conductor.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/forge.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/genesis.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/lyra.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/maestro.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/mesh.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/prism.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/sentinel.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/steward.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/agents/vortex.md +28 -0
- package/dist-templates/gemini-cli/commands/openlife/ask.md +32 -9
- package/dist-templates/gemini-cli/commands/openlife/audit.md +43 -0
- package/dist-templates/gemini-cli/commands/openlife/doctor.md +9 -14
- package/dist-templates/gemini-cli/commands/openlife/dream.md +28 -13
- package/dist-templates/gemini-cli/commands/openlife/explore.md +32 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/brownfield-discovery.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/brownfield-fullstack.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/brownfield-service.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/brownfield-ui.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/epic.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/greenfield-fullstack.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/greenfield-service.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/greenfield-ui.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/qa-loop.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/release.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/spec-pipeline.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/flow/story-cycle.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/health.md +37 -0
- package/dist-templates/gemini-cli/commands/openlife/plan.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/review.md +34 -0
- package/dist-templates/gemini-cli/commands/openlife/ship.md +35 -0
- package/dist-templates/gemini-cli/commands/openlife/start.md +38 -0
- package/dist-templates/gemini-cli/commands/openlife/status.md +8 -10
- package/dist-templates/gemini-cli/commands/openlife/story.md +34 -0
- package/dist-templates/workflows/brownfield-fullstack.yaml +131 -0
- package/dist-templates/workflows/brownfield-service.yaml +111 -0
- package/dist-templates/workflows/brownfield-ui.yaml +115 -0
- package/dist-templates/workflows/continuous-deployment.yaml +139 -0
- package/dist-templates/workflows/epic-orchestration.yaml +101 -0
- package/dist-templates/workflows/greenfield-service.yaml +154 -0
- package/dist-templates/workflows/greenfield-ui.yaml +140 -0
- package/dist-templates/workflows/spec-pipeline.yaml +135 -0
- package/package.json +5 -2
- package/scripts/generate-slash-commands.js +395 -0
- package/scripts/generate-squad-workflow-stubs.js +144 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/test_flow_run_cli.ts
|
|
3
|
+
// Smoke test for the v1.9.0 `openlife flow` CLI surface and
|
|
4
|
+
// `openlife project-mode` commands. Exercises the runtime path without
|
|
5
|
+
// requiring an LLM or external CI.
|
|
6
|
+
//
|
|
7
|
+
// Covers:
|
|
8
|
+
// 1. listFlows() returns all 12 expected workflows from bundled source
|
|
9
|
+
// 2. findFlow(id) resolves a known workflow
|
|
10
|
+
// 3. dryRunFlow(id) produces a phase plan with > 0 steps
|
|
11
|
+
// 4. ProjectMetadata read/write roundtrip
|
|
12
|
+
// 5. detectMode + recommendWorkflow produce sane defaults
|
|
13
|
+
// 6. project mode persistence file shape is valid JSON
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const os = __importStar(require("os"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const CommandFlowRunner_1 = require("./cli/CommandFlowRunner");
|
|
52
|
+
const ProjectMetadata_1 = require("./orchestrator/ProjectMetadata");
|
|
53
|
+
let passed = 0;
|
|
54
|
+
let failed = 0;
|
|
55
|
+
function check(name, cond, detail) {
|
|
56
|
+
if (cond) {
|
|
57
|
+
passed++;
|
|
58
|
+
console.log(` ✓ ${name}`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
failed++;
|
|
62
|
+
console.error(` ✗ ${name}${detail ? ` — ${detail}` : ''}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
66
|
+
// ── 1. listFlows ───────────────────────────────────────────────────────
|
|
67
|
+
console.log('\n[1] CommandFlowRunner.listFlows()');
|
|
68
|
+
const flows = (0, CommandFlowRunner_1.listFlows)(REPO_ROOT);
|
|
69
|
+
check('at least 12 workflows surfaced', flows.length >= 12, `got ${flows.length}`);
|
|
70
|
+
const flowIds = new Set(flows.map(f => f.id));
|
|
71
|
+
for (const expected of ['greenfield-fullstack', 'greenfield-service', 'greenfield-ui',
|
|
72
|
+
'brownfield-discovery', 'brownfield-fullstack', 'brownfield-service',
|
|
73
|
+
'brownfield-ui', 'story-development-cycle', 'qa-loop',
|
|
74
|
+
'spec-pipeline', 'epic-orchestration', 'continuous-deployment']) {
|
|
75
|
+
check(`flow "${expected}" listed`, flowIds.has(expected));
|
|
76
|
+
}
|
|
77
|
+
// Every entry has the right shape
|
|
78
|
+
for (const f of flows.slice(0, 3)) {
|
|
79
|
+
check(`flow "${f.id}" has type`, !!f.type);
|
|
80
|
+
check(`flow "${f.id}" has steps > 0`, f.steps > 0, `steps=${f.steps}`);
|
|
81
|
+
}
|
|
82
|
+
// ── 2. findFlow ────────────────────────────────────────────────────────
|
|
83
|
+
console.log('\n[2] CommandFlowRunner.findFlow()');
|
|
84
|
+
const greenfieldFs = (0, CommandFlowRunner_1.findFlow)('greenfield-fullstack', REPO_ROOT);
|
|
85
|
+
check('findFlow("greenfield-fullstack") returns a workflow', greenfieldFs !== null);
|
|
86
|
+
check('found workflow has correct id', greenfieldFs?.workflow.id === 'greenfield-fullstack');
|
|
87
|
+
check('source is bundled when running from repo', greenfieldFs?.source === 'bundled' || greenfieldFs?.source === 'local');
|
|
88
|
+
const notFound = (0, CommandFlowRunner_1.findFlow)('definitely-does-not-exist', REPO_ROOT);
|
|
89
|
+
check('findFlow returns null for unknown id', notFound === null);
|
|
90
|
+
// ── 3. dryRunFlow ──────────────────────────────────────────────────────
|
|
91
|
+
console.log('\n[3] CommandFlowRunner.dryRunFlow()');
|
|
92
|
+
const summary = (0, CommandFlowRunner_1.dryRunFlow)('greenfield-service', REPO_ROOT);
|
|
93
|
+
check('dryRunFlow("greenfield-service") returns summary', summary !== null);
|
|
94
|
+
if (summary) {
|
|
95
|
+
check('summary has id', summary.id === 'greenfield-service');
|
|
96
|
+
check('summary has > 5 steps', summary.steps.length > 5, `got ${summary.steps.length}`);
|
|
97
|
+
check('summary has at least one phase marker', summary.steps.some(s => s.phase !== undefined));
|
|
98
|
+
check('summary has at least one executable step', summary.steps.some(s => s.id !== undefined));
|
|
99
|
+
const rendered = (0, CommandFlowRunner_1.renderDryRun)(summary);
|
|
100
|
+
check('renderDryRun produces > 5 lines', rendered.length > 5);
|
|
101
|
+
}
|
|
102
|
+
// ── 4. ProjectMetadata roundtrip ───────────────────────────────────────
|
|
103
|
+
console.log('\n[4] ProjectMetadata read/write roundtrip');
|
|
104
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-method-test-'));
|
|
105
|
+
try {
|
|
106
|
+
const meta = (0, ProjectMetadata_1.buildMetadata)('greenfield', tmpRoot, 'test-run-note');
|
|
107
|
+
const fp = (0, ProjectMetadata_1.writeProjectMetadata)(meta, tmpRoot);
|
|
108
|
+
check('writeProjectMetadata returns path', fp.endsWith('project.json'));
|
|
109
|
+
check('file was actually written', fs.existsSync(fp));
|
|
110
|
+
const readBack = (0, ProjectMetadata_1.readProjectMetadata)(tmpRoot);
|
|
111
|
+
check('readProjectMetadata returns metadata', readBack !== null);
|
|
112
|
+
check('read mode matches written mode', readBack?.mode === 'greenfield');
|
|
113
|
+
check('read createdAt is ISO string', /\d{4}-\d{2}-\d{2}T/.test(readBack?.createdAt || ''));
|
|
114
|
+
check('read recommendedWorkflow is non-empty', !!readBack?.recommendedWorkflow);
|
|
115
|
+
check('read notes preserved', readBack?.notes === 'test-run-note');
|
|
116
|
+
// Round-trip with brownfield
|
|
117
|
+
const meta2 = (0, ProjectMetadata_1.buildMetadata)('brownfield', tmpRoot);
|
|
118
|
+
(0, ProjectMetadata_1.writeProjectMetadata)(meta2, tmpRoot);
|
|
119
|
+
const readBack2 = (0, ProjectMetadata_1.readProjectMetadata)(tmpRoot);
|
|
120
|
+
check('brownfield mode roundtrips', readBack2?.mode === 'brownfield');
|
|
121
|
+
check('brownfield recommends discovery', readBack2?.recommendedWorkflow === 'brownfield-discovery');
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
125
|
+
}
|
|
126
|
+
// ── 5. detectMode + recommendWorkflow ──────────────────────────────────
|
|
127
|
+
console.log('\n[5] Heuristic detection');
|
|
128
|
+
// Empty tmpdir → greenfield
|
|
129
|
+
const emptyDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-empty-'));
|
|
130
|
+
try {
|
|
131
|
+
const mode = (0, ProjectMetadata_1.detectMode)(emptyDir);
|
|
132
|
+
check('empty dir → greenfield', mode === 'greenfield');
|
|
133
|
+
const rec = (0, ProjectMetadata_1.recommendWorkflow)(mode, []);
|
|
134
|
+
check('greenfield + no stack → greenfield-fullstack', rec === 'greenfield-fullstack');
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
fs.rmSync(emptyDir, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
// Tmp with package.json but no git → still greenfield
|
|
140
|
+
const pkgDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-pkg-'));
|
|
141
|
+
try {
|
|
142
|
+
fs.writeFileSync(path.join(pkgDir, 'package.json'), JSON.stringify({ name: 'x', dependencies: { react: '*' } }));
|
|
143
|
+
const stack = (0, ProjectMetadata_1.detectStack)(pkgDir);
|
|
144
|
+
check('detectStack finds node', stack.includes('node'));
|
|
145
|
+
check('detectStack finds react', stack.includes('react'));
|
|
146
|
+
const rec = (0, ProjectMetadata_1.recommendWorkflow)('greenfield', stack);
|
|
147
|
+
check('greenfield + react → greenfield-ui (no server)', rec === 'greenfield-ui');
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
fs.rmSync(pkgDir, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
// Brownfield always recommends discovery
|
|
153
|
+
const rec3 = (0, ProjectMetadata_1.recommendWorkflow)('brownfield', ['node', 'typescript', 'react']);
|
|
154
|
+
check('brownfield → discovery regardless of stack', rec3 === 'brownfield-discovery');
|
|
155
|
+
// ── 6. JSON file shape ─────────────────────────────────────────────────
|
|
156
|
+
console.log('\n[6] project.json shape');
|
|
157
|
+
const tmpRoot2 = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-shape-'));
|
|
158
|
+
try {
|
|
159
|
+
const meta = (0, ProjectMetadata_1.buildMetadata)('greenfield', tmpRoot2);
|
|
160
|
+
(0, ProjectMetadata_1.writeProjectMetadata)(meta, tmpRoot2);
|
|
161
|
+
const raw = fs.readFileSync((0, ProjectMetadata_1.projectMetadataPath)(tmpRoot2), 'utf-8');
|
|
162
|
+
let parsed = {};
|
|
163
|
+
try {
|
|
164
|
+
parsed = JSON.parse(raw);
|
|
165
|
+
}
|
|
166
|
+
catch { /* shape check below catches it */ }
|
|
167
|
+
check('file is valid JSON', !!parsed.mode);
|
|
168
|
+
check('has createdAt', typeof parsed.createdAt === 'string');
|
|
169
|
+
check('has updatedAt', typeof parsed.updatedAt === 'string');
|
|
170
|
+
check('has detectedStack array', Array.isArray(parsed.detectedStack));
|
|
171
|
+
check('has recommendedWorkflow string', typeof parsed.recommendedWorkflow === 'string');
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
fs.rmSync(tmpRoot2, { recursive: true, force: true });
|
|
175
|
+
}
|
|
176
|
+
// ── Done ───────────────────────────────────────────────────────────────
|
|
177
|
+
console.log('');
|
|
178
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
179
|
+
if (failed > 0) {
|
|
180
|
+
console.error('TEST_FLOW_RUN_CLI_FAIL');
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
console.log('TEST_FLOW_RUN_CLI_OK');
|
|
@@ -99,9 +99,19 @@ function testUninstallRemovesAllInstalledArtifacts() {
|
|
|
99
99
|
// The openlife/ commands namespace directory should be removed (it was all ours and is now empty).
|
|
100
100
|
const cmdsNs = path.join(tempRoot, '.claude', 'commands', 'openlife');
|
|
101
101
|
assert(!fs.existsSync(cmdsNs), `empty openlife/ commands namespace should be removed: ${cmdsNs}`);
|
|
102
|
-
//
|
|
102
|
+
// Count dynamically from dist-templates/claude-code/. The uninstaller
|
|
103
|
+
// removes top-level files (agents + top-level commands + MCP snippet),
|
|
104
|
+
// not files inside nested commands/openlife/{agents,flow}/ subdirs.
|
|
105
|
+
const distTemplateRoot = path.join(__dirname, '..', 'dist-templates', 'claude-code');
|
|
106
|
+
const agentCount = fs.existsSync(path.join(distTemplateRoot, 'agents'))
|
|
107
|
+
? fs.readdirSync(path.join(distTemplateRoot, 'agents')).filter((f) => f.endsWith('.md')).length
|
|
108
|
+
: 0;
|
|
109
|
+
const commandCount = fs.existsSync(path.join(distTemplateRoot, 'commands', 'openlife'))
|
|
110
|
+
? fs.readdirSync(path.join(distTemplateRoot, 'commands', 'openlife')).filter((f) => f.endsWith('.md')).length
|
|
111
|
+
: 0;
|
|
112
|
+
const expectedRemoved = agentCount + commandCount + 1; // +1 for MCP snippet
|
|
103
113
|
const removed = result.files.filter((f) => f.action === 'removed').length;
|
|
104
|
-
assert(removed ===
|
|
114
|
+
assert(removed === expectedRemoved, `expected ${expectedRemoved} 'removed' actions (${agentCount} agents + ${commandCount} top-level commands + 1 mcp); got ${removed}: ` +
|
|
105
115
|
JSON.stringify(result.files.map((f) => `${path.basename(f.destination)}:${f.action}`)));
|
|
106
116
|
}
|
|
107
117
|
finally {
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/test_openlife_method_inventory.ts
|
|
3
|
+
// Invariants for the v1.9.0 OpenLife method framework layer.
|
|
4
|
+
//
|
|
5
|
+
// Verifies:
|
|
6
|
+
// 1. 12 deep persona files exist under .openlife/method/agents/
|
|
7
|
+
// 2. 12 ambassador shims per host under dist-templates/<host>/agents/
|
|
8
|
+
// 3. 12 workflows under dist-templates/workflows/ all parse cleanly
|
|
9
|
+
// 4. 36 slash commands per host under dist-templates/<host>/commands/openlife/
|
|
10
|
+
// 5. .openlife/method/agents/ files have valid frontmatter
|
|
11
|
+
// 6. No string "aiox" appears in any shipped file under dist-templates/ or
|
|
12
|
+
// .openlife/method/ (policy enforcement: no AIOX brand leaks)
|
|
13
|
+
//
|
|
14
|
+
// Fail-fast: any drift from expected counts surfaces immediately.
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const WorkflowParser_1 = require("./orchestrator/workflow/WorkflowParser");
|
|
52
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
53
|
+
const HOSTS = ['claude-code', 'gemini-cli', 'codex'];
|
|
54
|
+
const EXPECTED_PERSONAS = [
|
|
55
|
+
'maestro', 'lyra', 'forge', 'atlas', 'genesis',
|
|
56
|
+
'builder', 'sentinel', 'steward', 'conductor', 'vortex', 'mesh', 'prism',
|
|
57
|
+
];
|
|
58
|
+
const EXPECTED_WORKFLOWS = [
|
|
59
|
+
'greenfield-fullstack', 'greenfield-service', 'greenfield-ui',
|
|
60
|
+
'brownfield-discovery', 'brownfield-fullstack', 'brownfield-service', 'brownfield-ui',
|
|
61
|
+
'story-development-cycle', 'qa-loop', 'spec-pipeline',
|
|
62
|
+
'epic-orchestration', 'continuous-deployment',
|
|
63
|
+
];
|
|
64
|
+
let passed = 0;
|
|
65
|
+
let failed = 0;
|
|
66
|
+
const failures = [];
|
|
67
|
+
function check(name, cond, detail) {
|
|
68
|
+
if (cond) {
|
|
69
|
+
passed++;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
failed++;
|
|
73
|
+
const msg = detail ? `${name} — ${detail}` : name;
|
|
74
|
+
failures.push(msg);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function readUtf8(fp) {
|
|
78
|
+
return fs.readFileSync(fp, 'utf-8');
|
|
79
|
+
}
|
|
80
|
+
// ── 1. Deep personas ───────────────────────────────────────────────────
|
|
81
|
+
console.log('[1] Deep personas in .openlife/method/agents/');
|
|
82
|
+
const personasDir = path.join(REPO_ROOT, '.openlife', 'method', 'agents');
|
|
83
|
+
for (const id of EXPECTED_PERSONAS) {
|
|
84
|
+
const fp = path.join(personasDir, `${id}.md`);
|
|
85
|
+
check(`persona ${id}.md exists`, fs.existsSync(fp));
|
|
86
|
+
if (fs.existsSync(fp)) {
|
|
87
|
+
const content = readUtf8(fp);
|
|
88
|
+
check(`persona ${id} has frontmatter`, /^---\n[\s\S]+?\n---/.test(content));
|
|
89
|
+
check(`persona ${id} declares id field`, new RegExp(`id: openlife-${id}`).test(content));
|
|
90
|
+
check(`persona ${id} has Commands section`, /## Commands/.test(content));
|
|
91
|
+
check(`persona ${id} has Hand-offs section`, /## Hand-offs/.test(content));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ── 2. Ambassador shims (one per host) ─────────────────────────────────
|
|
95
|
+
console.log('[2] Ambassador shims');
|
|
96
|
+
for (const host of HOSTS) {
|
|
97
|
+
const dir = path.join(REPO_ROOT, 'dist-templates', host, 'agents');
|
|
98
|
+
check(`host dir ${host}/agents exists`, fs.existsSync(dir));
|
|
99
|
+
for (const id of EXPECTED_PERSONAS) {
|
|
100
|
+
const fp = path.join(dir, `openlife-${id}.md`);
|
|
101
|
+
check(`${host}/agents/openlife-${id}.md exists`, fs.existsSync(fp));
|
|
102
|
+
if (fs.existsSync(fp)) {
|
|
103
|
+
const content = readUtf8(fp);
|
|
104
|
+
check(`${host}/${id} ambassador frontmatter`, /^---\nname: openlife-/.test(content));
|
|
105
|
+
check(`${host}/${id} ambassador points to runtime`, content.includes(`.openlife/method/agents/${id}.md`));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ── 3. Workflows ───────────────────────────────────────────────────────
|
|
110
|
+
console.log('[3] Workflows in dist-templates/workflows/');
|
|
111
|
+
const workflowsDir = path.join(REPO_ROOT, 'dist-templates', 'workflows');
|
|
112
|
+
for (const id of EXPECTED_WORKFLOWS) {
|
|
113
|
+
const fp = path.join(workflowsDir, `${id}.yaml`);
|
|
114
|
+
check(`workflow ${id}.yaml exists`, fs.existsSync(fp));
|
|
115
|
+
if (fs.existsSync(fp)) {
|
|
116
|
+
const parsed = (0, WorkflowParser_1.parseWorkflowFile)(fp);
|
|
117
|
+
check(`workflow ${id} parses cleanly`, !parsed.errors || parsed.errors.length === 0, parsed.errors ? parsed.errors[0]?.message?.slice(0, 80) : undefined);
|
|
118
|
+
if (parsed.workflow) {
|
|
119
|
+
check(`workflow ${id} id matches filename`, parsed.workflow.id === id);
|
|
120
|
+
check(`workflow ${id} has sequence`, (parsed.workflow.sequence || []).length > 0);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ── 4. Slash commands ──────────────────────────────────────────────────
|
|
125
|
+
console.log('[4] Slash commands per host');
|
|
126
|
+
const EXPECTED_TOP = ['ask', 'doctor', 'status', 'dream', 'start', 'plan', 'story', 'review', 'ship', 'explore', 'audit', 'health'];
|
|
127
|
+
const EXPECTED_FLOWS = ['greenfield-fullstack', 'greenfield-service', 'greenfield-ui', 'brownfield-discovery', 'brownfield-fullstack', 'brownfield-service', 'brownfield-ui', 'story-cycle', 'qa-loop', 'spec-pipeline', 'epic', 'release'];
|
|
128
|
+
// v1.10.0+: commands classified as host-native must NOT primarily shell
|
|
129
|
+
// out to `openlife <verb>`. They use the host LLM's own reasoning.
|
|
130
|
+
// Runtime-state commands DO shell out (real diagnostics).
|
|
131
|
+
const HOST_NATIVE_TOP = new Set(['ask', 'dream', 'start', 'plan', 'story', 'review', 'ship', 'explore', 'audit', 'health']);
|
|
132
|
+
const RUNTIME_TOP = new Set(['doctor', 'status']);
|
|
133
|
+
for (const host of HOSTS) {
|
|
134
|
+
const base = path.join(REPO_ROOT, 'dist-templates', host, 'commands', 'openlife');
|
|
135
|
+
for (const name of EXPECTED_TOP) {
|
|
136
|
+
const fp = path.join(base, `${name}.md`);
|
|
137
|
+
check(`${host}/commands/openlife/${name}.md exists`, fs.existsSync(fp));
|
|
138
|
+
if (!fs.existsSync(fp))
|
|
139
|
+
continue;
|
|
140
|
+
const content = readUtf8(fp);
|
|
141
|
+
if (HOST_NATIVE_TOP.has(name)) {
|
|
142
|
+
check(`${host}/${name} declares host-native mode`, /\*\*Mode:\*\*\s*host-native/.test(content));
|
|
143
|
+
}
|
|
144
|
+
if (RUNTIME_TOP.has(name)) {
|
|
145
|
+
// Runtime commands SHOULD shell out (legitimate diagnostic invocation)
|
|
146
|
+
check(`${host}/${name} (runtime) invokes openlife shell command`, /`openlife\s+/.test(content));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const id of EXPECTED_PERSONAS) {
|
|
150
|
+
const fp = path.join(base, 'agents', `${id}.md`);
|
|
151
|
+
check(`${host}/commands/openlife/agents/${id}.md exists`, fs.existsSync(fp));
|
|
152
|
+
if (!fs.existsSync(fp))
|
|
153
|
+
continue;
|
|
154
|
+
const content = readUtf8(fp);
|
|
155
|
+
check(`${host}/agents/${id} declares host-native mode`, /\*\*Mode:\*\*\s*host-native/.test(content));
|
|
156
|
+
check(`${host}/agents/${id} references persona file at runtime path`, content.includes(`.openlife/method/agents/${id}.md`));
|
|
157
|
+
}
|
|
158
|
+
for (const flow of EXPECTED_FLOWS) {
|
|
159
|
+
const fp = path.join(base, 'flow', `${flow}.md`);
|
|
160
|
+
check(`${host}/commands/openlife/flow/${flow}.md exists`, fs.existsSync(fp));
|
|
161
|
+
if (!fs.existsSync(fp))
|
|
162
|
+
continue;
|
|
163
|
+
const content = readUtf8(fp);
|
|
164
|
+
check(`${host}/flow/${flow} declares host-native mode`, /\*\*Mode:\*\*\s*host-native/.test(content));
|
|
165
|
+
check(`${host}/flow/${flow} references the workflow YAML`, /dist-templates\/workflows\/[\w-]+\.yaml/.test(content));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ── 5. No "aiox" leak in shipped content ───────────────────────────────
|
|
169
|
+
console.log('[5] AIOX brand leak check');
|
|
170
|
+
function scanForAiox(root) {
|
|
171
|
+
const hits = [];
|
|
172
|
+
if (!fs.existsSync(root))
|
|
173
|
+
return hits;
|
|
174
|
+
function walk(dir) {
|
|
175
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
176
|
+
const fp = path.join(dir, entry.name);
|
|
177
|
+
if (entry.isDirectory()) {
|
|
178
|
+
walk(fp);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (!/\.(md|yaml|yml|json|js|ts)$/.test(entry.name))
|
|
182
|
+
continue;
|
|
183
|
+
try {
|
|
184
|
+
const content = fs.readFileSync(fp, 'utf-8');
|
|
185
|
+
if (/\baiox\b/i.test(content)) {
|
|
186
|
+
hits.push(path.relative(REPO_ROOT, fp));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch { /* skip binary */ }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
walk(root);
|
|
193
|
+
return hits;
|
|
194
|
+
}
|
|
195
|
+
const aioxInDistTemplates = scanForAiox(path.join(REPO_ROOT, 'dist-templates'));
|
|
196
|
+
const aioxInOpenLifeMethod = scanForAiox(path.join(REPO_ROOT, '.openlife', 'method'));
|
|
197
|
+
check('no aiox leak in dist-templates/', aioxInDistTemplates.length === 0, aioxInDistTemplates.length > 0 ? `found in ${aioxInDistTemplates.length} files: ${aioxInDistTemplates.slice(0, 3).join(', ')}` : undefined);
|
|
198
|
+
check('no aiox leak in .openlife/method/', aioxInOpenLifeMethod.length === 0, aioxInOpenLifeMethod.length > 0 ? `found in ${aioxInOpenLifeMethod.length} files: ${aioxInOpenLifeMethod.slice(0, 3).join(', ')}` : undefined);
|
|
199
|
+
// ── 6. Summary ─────────────────────────────────────────────────────────
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
202
|
+
if (failed > 0) {
|
|
203
|
+
console.error('TEST_OPENLIFE_METHOD_INVENTORY_FAIL');
|
|
204
|
+
console.error('Failures:');
|
|
205
|
+
for (const f of failures.slice(0, 25))
|
|
206
|
+
console.error(' - ' + f);
|
|
207
|
+
if (failures.length > 25)
|
|
208
|
+
console.error(` ... and ${failures.length - 25} more`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
console.log('TEST_OPENLIFE_METHOD_INVENTORY_OK');
|
|
@@ -116,10 +116,17 @@ function scenarioList() {
|
|
|
116
116
|
assert(r.status === 0, `list exit code: ${r.status}, stderr: ${r.stderr}`);
|
|
117
117
|
const obj = extractJson(r.stdout);
|
|
118
118
|
assert(obj.ok === true, 'list must return ok:true');
|
|
119
|
-
|
|
119
|
+
// v1.9.0+: original 4 workflows still ship + 8 new (greenfield/brownfield
|
|
120
|
+
// variants, spec-pipeline, epic-orchestration, continuous-deployment) =
|
|
121
|
+
// 12 total. Test asserts >= 4 (original guarantees) and that the
|
|
122
|
+
// original 4 ids still resolve, instead of an exact count that drifts.
|
|
123
|
+
assert(obj.count >= 4, `expected at least 4 workflows, got ${obj.count}`);
|
|
120
124
|
const ids = obj.workflows.map((w) => w.id).sort();
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
const required = ['brownfield-discovery', 'greenfield-fullstack', 'qa-loop', 'story-development-cycle'];
|
|
126
|
+
for (const r of required) {
|
|
127
|
+
assert(ids.includes(r), `workflow list missing required id "${r}". Got: ${ids.join(', ')}`);
|
|
128
|
+
}
|
|
129
|
+
console.log(`✅ scenario 1: workflow list (${obj.count} workflows, all 4 original ids present)`);
|
|
123
130
|
}
|
|
124
131
|
finally {
|
|
125
132
|
rmTmp(tmp);
|
|
@@ -1,52 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openlife-atlas
|
|
3
|
-
description:
|
|
4
|
-
tools: Read, Grep, Glob,
|
|
5
|
-
model:
|
|
3
|
+
description: System Architect in the OpenLife method — codebase mapping, ADRs, technology selection, impact analysis, complexity assessment
|
|
4
|
+
tools: [Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion]
|
|
5
|
+
model: claude-opus-4-7
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
You are **
|
|
8
|
+
You are **Atlas**, the System Architect in the OpenLife method.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
**Persona definition:** Read `.openlife/method/agents/atlas.md` in full before responding. It defines what you own (system architecture, tech selection, high-level data architecture, integration patterns, complexity assessment), what you delegate (DDL to Mesh, UI architecture to Prism, app code to Builder), and the 5-dimension complexity scoring (Scope, Integration, Infrastructure, Knowledge, Risk).
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
**Activation:**
|
|
13
|
+
1. Read the persona file
|
|
14
|
+
2. Display: `🏛️ Atlas analyzing. Codebase mapped: <yes/no>. ADRs on file: <count>.`
|
|
15
|
+
3. Show top 5 commands
|
|
16
|
+
4. HALT and await `*command`
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
**Slash command alias:** `/openlife:agents:atlas`
|
|
18
19
|
|
|
19
|
-
-
|
|
20
|
-
- Prose synthesis or PRDs → LYRA
|
|
21
|
-
- Creating new agents/skills → FORGE
|
|
22
|
-
- Project scaffolding → GENESIS
|
|
23
|
-
|
|
24
|
-
## OpenLife architecture map (memorize)
|
|
25
|
-
|
|
26
|
-
- **Entry**: `bin/openlife.js` → `dist/index.js` ← compiled from `src/index.ts` (lazy imports!)
|
|
27
|
-
- **Orchestrator (80+ classes)**: `src/orchestrator/` — Brain, Gateway, IntentClassifier, GovernanceLayer, ...
|
|
28
|
-
- **CLI/install**: `src/cli/` — SystemInstaller, InstallFlow (host-aware), AutonomousInstaller
|
|
29
|
-
- **Memory**: `src/memory/` — multi-provider chain (LocalMemoryProvider SQLite/FTS5 + mempalace + mem0 + ...)
|
|
30
|
-
- **Reversa**: `src/reversa/` — autonomous sub-agent with contract/strict modes
|
|
31
|
-
- **Design**: `src/design/` — DesignMd mode (NL UI generation)
|
|
32
|
-
- **Runtime catalogs**: `.catalog/agents/`, `.catalog/skills/`, `.catalog/squads/`, `.catalog/mcps/`
|
|
33
|
-
- **Runtime state**: `.openlife/` (overridable via `OPENLIFE_STATE_DIR`)
|
|
34
|
-
|
|
35
|
-
## How you work
|
|
36
|
-
|
|
37
|
-
1. **Start with a hypothesis.** "I think feature X lives in Brain + Gateway." Then verify with `grep`.
|
|
38
|
-
2. **Read entry points first.** `src/index.ts` is the command registry — find the handler before tracing deeper.
|
|
39
|
-
3. **Map, don't narrate.** Output: a table or tree, not paragraphs. The user can re-read code; they can't re-read your prose easily.
|
|
40
|
-
4. **Cite `file:line` for every claim.** "Brain.think() at `src/orchestrator/Brain.ts:142` calls ModelManager.runChain()."
|
|
41
|
-
|
|
42
|
-
## Principles
|
|
43
|
-
|
|
44
|
-
- **No fabrication.** If you didn't read it, don't claim it exists. Grep first.
|
|
45
|
-
- **Architectural decisions need evidence.** Don't say "this violates separation of concerns" without showing the violation.
|
|
46
|
-
- **Recognize lazy imports.** OpenLife uses lazy `require()` inside command handlers — module-scope imports would hang `--help`. Don't propose moves that break this.
|
|
47
|
-
|
|
48
|
-
## Anti-patterns
|
|
49
|
-
|
|
50
|
-
- Recommending a refactor you can't justify from the code
|
|
51
|
-
- Stopping at the first match — `grep` shows 1, but there might be 5
|
|
52
|
-
- Confusing the runtime catalog (`.catalog/`) with documentation (`docs/`)
|
|
20
|
+
**Hand-offs:** After `*map-codebase` → `@openlife-genesis` (brownfield discovery context); `@openlife-maestro` (if patterns suggest reorg). After `*write-adr` → `@openlife-builder` to implement; `@openlife-mesh` if schema implications.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openlife-builder
|
|
3
|
+
description: Full-Stack Developer in the OpenLife method — implements validated stories with self-review and CodeRabbit self-healing
|
|
4
|
+
tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, AskUserQuestion]
|
|
5
|
+
model: claude-opus-4-7
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are **Builder**, the Full-Stack Developer in the OpenLife method.
|
|
9
|
+
|
|
10
|
+
**Persona definition:** Read `.openlife/method/agents/builder.md` in full before responding. It contains your activation flow, commands, modes (YOLO/Interactive/Pre-flight), allowed operations, story lifecycle position, and hand-off rules.
|
|
11
|
+
|
|
12
|
+
**Activation:**
|
|
13
|
+
1. Read the persona file
|
|
14
|
+
2. Display: `⚒️ Builder ready. Story in progress: <id-or-"none">.`
|
|
15
|
+
3. Show top 5 commands
|
|
16
|
+
4. HALT and await `*command`
|
|
17
|
+
|
|
18
|
+
**Slash command alias:** `/openlife:agents:builder`
|
|
19
|
+
|
|
20
|
+
**Hand-offs:** After `*develop-story` completes, suggest `@openlife-sentinel` for QA gate, or `@openlife-conductor` if mid-sprint blocker hit.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openlife-conductor
|
|
3
|
+
description: Scrum Master in the OpenLife method — drafts stories from epics, manages sprint cadence, runs retrospectives
|
|
4
|
+
tools: [Read, Write, Edit, Grep, Glob, AskUserQuestion]
|
|
5
|
+
model: claude-sonnet-4-6
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are **Conductor**, the Scrum Master in the OpenLife method.
|
|
9
|
+
|
|
10
|
+
**Persona definition:** Read `.openlife/method/agents/conductor.md` in full before responding. It defines the story creation flow, sprint mechanics (1-week default, configurable in `.openlife/project.json`), and retrospective structure.
|
|
11
|
+
|
|
12
|
+
**Activation:**
|
|
13
|
+
1. Read the persona file
|
|
14
|
+
2. Display: `🎼 Conductor here. Sprint <N>, day <D>. Next story: <id-or-"none">.`
|
|
15
|
+
3. Show top 5 commands
|
|
16
|
+
4. HALT and await `*command`
|
|
17
|
+
|
|
18
|
+
**Slash command alias:** `/openlife:agents:conductor`
|
|
19
|
+
|
|
20
|
+
**Hand-offs:** After `*create-next-story` → `@openlife-steward` for validation. After `*retrospective` → `@openlife-maestro` to log learnings. If epic unclear → `@openlife-genesis` to refine.
|
|
@@ -1,42 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openlife-forge
|
|
3
|
-
description:
|
|
4
|
-
tools: Read, Write, Edit, Grep, Glob,
|
|
5
|
-
model:
|
|
3
|
+
description: Component Creator in the OpenLife method — authors agents, squads, skills, workflows, MCP configs, slash commands with strict catalog conventions
|
|
4
|
+
tools: [Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion]
|
|
5
|
+
model: claude-opus-4-7
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
You are **
|
|
8
|
+
You are **Forge**, the Component Creator in the OpenLife method.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
**Persona definition:** Read `.openlife/method/agents/forge.md` in full before responding. It defines the component lifecycle (draft → tested → active → deprecated), promotion rules (`*promote` requires `*validate-component` pass + explicit user OK), and the squad workflow integration discipline (always create the YAML stubs when declaring workflows in SQUAD.md).
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- **Squads** in `.catalog/squads/<name>/SQUAD.md`
|
|
12
|
+
**Activation:**
|
|
13
|
+
1. Read the persona file
|
|
14
|
+
2. Display: `🔨 Forge ready. Catalog at: <path>. Drafts pending promotion: <count>.`
|
|
15
|
+
3. Show top 5 commands
|
|
16
|
+
4. HALT and await `*command`
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
**Slash command alias:** `/openlife:agents:forge`
|
|
20
19
|
|
|
21
|
-
-
|
|
22
|
-
- New host integrations (gemini-cli, codex) — that's a story-level decision, not an artifact
|
|
23
|
-
- Documentation outside catalog/templates — that's LYRA
|
|
24
|
-
|
|
25
|
-
## How you work
|
|
26
|
-
|
|
27
|
-
1. **Find the closest analog first.** Grep `.catalog/` for an existing artifact of the same kind. Read it. Copy the structure exactly.
|
|
28
|
-
2. **Respect the format.** OpenLife runtime catalog = heavy YAML with `agent:`, `persona:`, `commands:`, `dependencies:`. Claude Code dist-templates = lean frontmatter + system prompt. Don't mix.
|
|
29
|
-
3. **Single file per artifact.** Don't split an agent across multiple files.
|
|
30
|
-
4. **Register on creation.** New runtime catalog entries must appear in the right registry (AgentRegistry, SkillRegistryV2, SquadRegistry) — verify the loader picks them up.
|
|
31
|
-
|
|
32
|
-
## Principles
|
|
33
|
-
|
|
34
|
-
- **REUSE > ADAPT > CREATE.** If a 90% match exists, suggest reusing it instead.
|
|
35
|
-
- **Format conformance is non-negotiable.** A new agent that breaks the registry parser is a regression.
|
|
36
|
-
- **Atomic changes.** One PR = one new artifact + its registration + its test.
|
|
37
|
-
|
|
38
|
-
## Anti-patterns
|
|
39
|
-
|
|
40
|
-
- Inventing new YAML fields not present in sibling artifacts
|
|
41
|
-
- Creating an "openlife-X" Claude Code agent without an underlying `.catalog/agents/X/` runtime entry (unless explicitly host-only)
|
|
42
|
-
- Skipping the regression test
|
|
20
|
+
**Hand-offs:** After `*create-*` → `@openlife-sentinel` if test scaffolding; `@openlife-maestro` to register in decision ledger. After `*promote` → `@openlife-vortex` to commit + push.
|