@open-agent-toolkit/cli 0.0.23 → 0.0.24

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 CHANGED
@@ -27,9 +27,20 @@ Additional useful entry points:
27
27
 
28
28
  - `oat tools install`
29
29
  - `oat docs init --app-name my-docs`
30
+ - `oat config dump --json`
31
+ - `oat project status --json`
32
+ - `oat project list --json`
30
33
  - `oat project archive sync`
31
34
  - `oat doctor`
32
35
 
36
+ ## Inspection Commands
37
+
38
+ Use these commands when you want structured runtime/project state out of the CLI:
39
+
40
+ - `oat config dump --json` - emit merged OAT config with per-key source attribution
41
+ - `oat project status --json` - emit the active project's full parsed control-plane state
42
+ - `oat project list --json` - emit summary state for tracked projects under the configured projects root
43
+
33
44
  ## Requirements
34
45
 
35
46
  - Node.js `>=22.17.0`
@@ -45,9 +45,12 @@ Use `oat config` for repo runtime config inspection and supported key mutation.
45
45
  - `oat config get <key>` - read one resolved config value
46
46
  - `oat config set <key> <value>` - update a supported shared or repo-local key
47
47
  - `oat config list` - show the resolved command-surface values with source information
48
+ - `oat config dump --json` - emit the full merged config payload with per-key source attribution, suitable for automation and debugging
48
49
  - `oat config describe` - list supported config surfaces and keys across shared repo, repo-local, user, and sync/provider config
49
50
  - `oat config describe <key>` - show file location, scope, default, mutability, and owning command for one key
50
51
 
52
+ Use `oat config dump --json` when you need the whole resolved config in one machine-readable response rather than a single key or a human-oriented list view.
53
+
51
54
  Archive lifecycle settings live here as shared repo config:
52
55
 
53
56
  - `archive.s3Uri`
@@ -22,14 +22,20 @@ The CLI is also a standalone value path. You can use `oat init`, `oat sync`, `oa
22
22
 
23
23
  ## Command Groups
24
24
 
25
- | Command group | What it covers | Go deeper |
26
- | ----------------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
27
- | `oat init` | Bootstrap canonical OAT directories, sync config, optional hooks, and guided setup. | [CLI Bootstrap](../cli-utilities/bootstrap.md) |
28
- | `oat tools ...` | Install, inspect, update, and remove bundled OAT tool packs and assets. | [Tool Packs](../cli-utilities/tool-packs.md) |
29
- | `oat backlog ...` / `oat local ...` | File-backed backlog helpers, local path sync, and local-only operational support. | [Config and Local State](../cli-utilities/config-and-local-state.md) |
30
- | `oat config ...` / `oat instructions ...` | Config discovery, supported mutations, and instruction-integrity helpers. | [Config and Local State](../cli-utilities/config-and-local-state.md) |
31
- | `oat state ...` / `oat index ...` / `internal` | Repo dashboard refresh, repo indexing, validation helpers, and diagnostics. | [Config and Local State](../cli-utilities/config-and-local-state.md) |
32
- | `oat docs ...` | Docs app bootstrap, migration, index generation, nav sync, and docs workflow entrypoints. | [Docs Tooling Commands](../docs-tooling/commands.md) |
33
- | `oat status` / `oat sync` / `oat providers ...` | Provider sync, drift inspection, provider configuration, and adoption behavior. | [Provider Sync](../provider-sync/index.md) |
34
- | `oat project ...` / `oat cleanup ...` | Project scaffolding, execution mode, and project/artifact cleanup commands. | [Workflow & Projects](../workflows/projects/index.md) |
35
- | `oat repo ...` | Repository-level analysis workflows, currently centered on PR comments. | [Repository Analysis](../workflows/projects/repo-analysis.md) |
25
+ | Command group | What it covers | Go deeper |
26
+ | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
27
+ | `oat init` | Bootstrap canonical OAT directories, sync config, optional hooks, and guided setup. | [CLI Bootstrap](../cli-utilities/bootstrap.md) |
28
+ | `oat tools ...` | Install, inspect, update, and remove bundled OAT tool packs and assets. | [Tool Packs](../cli-utilities/tool-packs.md) |
29
+ | `oat backlog ...` / `oat local ...` | File-backed backlog helpers, local path sync, and local-only operational support. | [Config and Local State](../cli-utilities/config-and-local-state.md) |
30
+ | `oat config ...` / `oat instructions ...` | Config discovery, source-aware config dumps, supported mutations, and instruction-integrity helpers. | [Config and Local State](../cli-utilities/config-and-local-state.md) |
31
+ | `oat state ...` / `oat index ...` / `internal` | Repo dashboard refresh, repo indexing, validation helpers, and diagnostics. | [Config and Local State](../cli-utilities/config-and-local-state.md) |
32
+ | `oat docs ...` | Docs app bootstrap, migration, index generation, nav sync, and docs workflow entrypoints. | [Docs Tooling Commands](../docs-tooling/commands.md) |
33
+ | `oat status` / `oat sync` / `oat providers ...` | Provider sync, drift inspection, provider configuration, and adoption behavior. | [Provider Sync](../provider-sync/index.md) |
34
+ | `oat project ...` / `oat cleanup ...` | Project scaffolding, active-project status inspection, tracked-project listing, execution mode, and project/artifact cleanup commands. | [Workflow & Projects](../workflows/projects/index.md) |
35
+ | `oat repo ...` | Repository-level analysis workflows, currently centered on PR comments. | [Repository Analysis](../workflows/projects/repo-analysis.md) |
36
+
37
+ Notable inspection commands introduced in the current CLI surface:
38
+
39
+ - `oat config dump --json` - merged config with source attribution
40
+ - `oat project status --json` - full parsed state for the active tracked project
41
+ - `oat project list --json` - summary state for tracked projects under the configured projects root
@@ -80,6 +80,7 @@ Archive sync surfaces:
80
80
 
81
81
  ## CLI code
82
82
 
83
+ - `packages/control-plane/` - read-only control-plane library for project-state parsing and recommendation
83
84
  - `packages/cli/src/commands/`
84
85
  - `packages/cli/src/commands/cleanup/`
85
86
  - `packages/cli/src/commands/cleanup/project/`
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli": "0.0.23",
3
- "docs-config": "0.0.23",
4
- "docs-theme": "0.0.23",
5
- "docs-transforms": "0.0.23"
2
+ "cli": "0.0.24",
3
+ "docs-config": "0.0.24",
4
+ "docs-theme": "0.0.24",
5
+ "docs-transforms": "0.0.24"
6
6
  }
@@ -1,7 +1,7 @@
1
1
  import { Command, Option } from 'commander';
2
2
  const PROGRAM_NAME = 'oat';
3
3
  const PROGRAM_DESCRIPTION = 'Open Agent Toolkit CLI for provider interoperability';
4
- const PROGRAM_VERSION = '0.0.18';
4
+ const PROGRAM_VERSION = '0.0.24';
5
5
  const SCOPE_CHOICES = ['project', 'user', 'all'];
6
6
  export function createProgram() {
7
7
  return new Command()
@@ -0,0 +1,12 @@
1
+ import { type CommandContext, type GlobalOptions } from '../../app/command-context.js';
2
+ import { type ResolvedConfig } from '../../config/resolve.js';
3
+ import { Command } from 'commander';
4
+ interface ConfigDumpDependencies {
5
+ buildCommandContext: (options: GlobalOptions) => CommandContext;
6
+ resolveProjectRoot: (cwd: string) => Promise<string>;
7
+ resolveEffectiveConfig: (repoRoot: string, userConfigDir: string, env: NodeJS.ProcessEnv) => Promise<ResolvedConfig>;
8
+ processEnv: NodeJS.ProcessEnv;
9
+ }
10
+ export declare function createConfigDumpCommand(overrides?: Partial<ConfigDumpDependencies>): Command;
11
+ export {};
12
+ //# sourceMappingURL=dump.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dump.d.ts","sourceRoot":"","sources":["../../../src/commands/config/dump.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE9E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,UAAU,sBAAsB;IAC9B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AA2ED,wBAAgB,uBAAuB,CACrC,SAAS,GAAE,OAAO,CAAC,sBAAsB,CAAM,GAC9C,OAAO,CAcT"}
@@ -0,0 +1,76 @@
1
+ import { join } from 'node:path';
2
+ import { buildCommandContext, } from '../../app/command-context.js';
3
+ import { readGlobalOptions } from '../shared/shared.utils.js';
4
+ import { resolveEffectiveConfig } from '../../config/resolve.js';
5
+ import { resolveProjectRoot } from '../../fs/paths.js';
6
+ import { Command } from 'commander';
7
+ const DEFAULT_DEPENDENCIES = {
8
+ buildCommandContext,
9
+ resolveProjectRoot,
10
+ resolveEffectiveConfig,
11
+ processEnv: process.env,
12
+ };
13
+ function formatConfigDump(result) {
14
+ const lines = [];
15
+ const grouped = new Map();
16
+ for (const [key, entry] of Object.entries(result.resolved).sort(([a], [b]) => a.localeCompare(b))) {
17
+ const items = grouped.get(entry.source) ?? [];
18
+ items.push(`${key} = ${String(entry.value)}`);
19
+ grouped.set(entry.source, items);
20
+ }
21
+ for (const source of ['shared', 'local', 'user', 'env', 'default']) {
22
+ const items = grouped.get(source);
23
+ if (!items || items.length === 0) {
24
+ continue;
25
+ }
26
+ if (lines.length > 0) {
27
+ lines.push('');
28
+ }
29
+ lines.push(`${source}:`);
30
+ for (const item of items) {
31
+ lines.push(` ${item}`);
32
+ }
33
+ }
34
+ return lines;
35
+ }
36
+ async function runConfigDump(context, dependencies) {
37
+ try {
38
+ const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
39
+ const userConfigDir = join(context.home, '.oat');
40
+ const result = await dependencies.resolveEffectiveConfig(repoRoot, userConfigDir, dependencies.processEnv);
41
+ if (context.json) {
42
+ context.logger.json({
43
+ status: 'ok',
44
+ ...result,
45
+ });
46
+ }
47
+ else {
48
+ for (const line of formatConfigDump(result)) {
49
+ context.logger.info(line);
50
+ }
51
+ }
52
+ process.exitCode = 0;
53
+ }
54
+ catch (error) {
55
+ const message = error instanceof Error ? error.message : String(error);
56
+ if (context.json) {
57
+ context.logger.json({ status: 'error', message });
58
+ }
59
+ else {
60
+ context.logger.error(message);
61
+ }
62
+ process.exitCode = 1;
63
+ }
64
+ }
65
+ export function createConfigDumpCommand(overrides = {}) {
66
+ const dependencies = {
67
+ ...DEFAULT_DEPENDENCIES,
68
+ ...overrides,
69
+ };
70
+ return new Command('dump')
71
+ .description('Dump merged OAT config with source attribution')
72
+ .action(async (_options, command) => {
73
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
74
+ await runConfigDump(context, dependencies);
75
+ });
76
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EACL,KAAK,SAAS,EACd,KAAK,cAAc,EAMpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2CpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAk3BD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAiET"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EACL,KAAK,SAAS,EACd,KAAK,cAAc,EAMpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6CpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAk3BD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAwET"}
@@ -4,6 +4,7 @@ import { readGlobalOptions } from '../shared/shared.utils.js';
4
4
  import { readOatConfig, readOatLocalConfig, writeOatConfig, writeOatLocalConfig, } from '../../config/oat-config.js';
5
5
  import { resolveProjectRoot } from '../../fs/paths.js';
6
6
  import { Command } from 'commander';
7
+ import { createConfigDumpCommand } from './dump.js';
7
8
  const KEY_ORDER = [
8
9
  'activeIdea',
9
10
  'activeProject',
@@ -797,6 +798,11 @@ export function createConfigCommand(overrides = {}) {
797
798
  .action(async (_options, command) => {
798
799
  const context = dependencies.buildCommandContext(readGlobalOptions(command));
799
800
  await runList(context, dependencies);
801
+ }))
802
+ .addCommand(createConfigDumpCommand({
803
+ buildCommandContext: dependencies.buildCommandContext,
804
+ resolveProjectRoot: dependencies.resolveProjectRoot,
805
+ processEnv: dependencies.processEnv,
800
806
  }))
801
807
  .addCommand(new Command('describe')
802
808
  .description('Describe supported OAT config surfaces and keys')
@@ -71,7 +71,7 @@ const OAT_DEP_PACKAGES = [
71
71
  'docs-theme',
72
72
  'docs-transforms',
73
73
  ];
74
- const DEFAULT_OAT_PUBLISHED_VERSION = '0.0.18';
74
+ const DEFAULT_OAT_PUBLISHED_VERSION = '0.0.24';
75
75
  const PUBLIC_PACKAGE_VERSIONS_FILE = 'public-package-versions.json';
76
76
  export async function detectIsOatRepo(repoRoot) {
77
77
  for (const pkg of OAT_DEP_PACKAGES) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,oBAAoB,IAAI,OAAO,CAQ9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,oBAAoB,IAAI,OAAO,CAU9C"}
@@ -1,15 +1,19 @@
1
1
  import { Command } from 'commander';
2
2
  import { createProjectArchiveCommand } from './archive/index.js';
3
+ import { createProjectListCommand } from './list.js';
3
4
  import { createProjectNewCommand } from './new/index.js';
4
5
  import { createProjectOpenCommand } from './open/index.js';
5
6
  import { createProjectPauseCommand } from './pause/index.js';
6
7
  import { createProjectSetModeCommand } from './set-mode/index.js';
8
+ import { createProjectStatusCommand } from './status.js';
7
9
  export function createProjectCommand() {
8
10
  return new Command('project')
9
11
  .description('Manage OAT project workflows')
10
12
  .addCommand(createProjectArchiveCommand())
13
+ .addCommand(createProjectListCommand())
11
14
  .addCommand(createProjectNewCommand())
12
15
  .addCommand(createProjectOpenCommand())
13
16
  .addCommand(createProjectPauseCommand())
14
- .addCommand(createProjectSetModeCommand());
17
+ .addCommand(createProjectSetModeCommand())
18
+ .addCommand(createProjectStatusCommand());
15
19
  }
@@ -0,0 +1,13 @@
1
+ import { type CommandContext, type GlobalOptions } from '../../app/command-context.js';
2
+ import { type ProjectSummary } from '@open-agent-toolkit/control-plane';
3
+ import { Command } from 'commander';
4
+ interface ProjectListDependencies {
5
+ buildCommandContext: (options: GlobalOptions) => CommandContext;
6
+ resolveProjectRoot: (cwd: string) => Promise<string>;
7
+ resolveProjectsRoot: (repoRoot: string, env: NodeJS.ProcessEnv) => Promise<string>;
8
+ listProjects: (projectsRoot: string) => Promise<ProjectSummary[]>;
9
+ processEnv: NodeJS.ProcessEnv;
10
+ }
11
+ export declare function createProjectListCommand(overrides?: Partial<ProjectListDependencies>): Command;
12
+ export {};
13
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/project/list.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAClE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAgGD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAcT"}
@@ -0,0 +1,91 @@
1
+ import { isAbsolute, join } from 'node:path';
2
+ import { buildCommandContext, } from '../../app/command-context.js';
3
+ import { resolveProjectsRoot } from '../shared/oat-paths.js';
4
+ import { readGlobalOptions } from '../shared/shared.utils.js';
5
+ import { resolveProjectRoot } from '../../fs/paths.js';
6
+ import { listProjects, } from '@open-agent-toolkit/control-plane';
7
+ import { Command } from 'commander';
8
+ const DEFAULT_DEPENDENCIES = {
9
+ buildCommandContext,
10
+ resolveProjectRoot,
11
+ resolveProjectsRoot,
12
+ listProjects,
13
+ processEnv: process.env,
14
+ };
15
+ function formatProjectTable(projects) {
16
+ if (projects.length === 0) {
17
+ return ['No tracked projects found.'];
18
+ }
19
+ const rows = projects.map((project) => ({
20
+ name: project.name,
21
+ phase: `${project.phase} (${project.phaseStatus})`,
22
+ progress: `${project.progress.completed}/${project.progress.total}`,
23
+ recommendation: project.recommendation.skill,
24
+ }));
25
+ const widths = {
26
+ name: Math.max('NAME'.length, ...rows.map((row) => row.name.length)),
27
+ phase: Math.max('PHASE'.length, ...rows.map((row) => row.phase.length)),
28
+ progress: Math.max('PROGRESS'.length, ...rows.map((row) => row.progress.length)),
29
+ recommendation: Math.max('RECOMMENDATION'.length, ...rows.map((row) => row.recommendation.length)),
30
+ };
31
+ const header = [
32
+ 'NAME'.padEnd(widths.name),
33
+ 'PHASE'.padEnd(widths.phase),
34
+ 'PROGRESS'.padEnd(widths.progress),
35
+ 'RECOMMENDATION'.padEnd(widths.recommendation),
36
+ ].join(' ');
37
+ const divider = [
38
+ '-'.repeat(widths.name),
39
+ '-'.repeat(widths.phase),
40
+ '-'.repeat(widths.progress),
41
+ '-'.repeat(widths.recommendation),
42
+ ].join(' ');
43
+ const lines = rows.map((row) => [
44
+ row.name.padEnd(widths.name),
45
+ row.phase.padEnd(widths.phase),
46
+ row.progress.padEnd(widths.progress),
47
+ row.recommendation.padEnd(widths.recommendation),
48
+ ].join(' '));
49
+ return [header, divider, ...lines];
50
+ }
51
+ async function runProjectList(context, dependencies) {
52
+ try {
53
+ const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
54
+ const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
55
+ const absoluteProjectsRoot = isAbsolute(projectsRoot)
56
+ ? projectsRoot
57
+ : join(repoRoot, projectsRoot);
58
+ const projects = await dependencies.listProjects(absoluteProjectsRoot);
59
+ if (context.json) {
60
+ context.logger.json({ status: 'ok', projects });
61
+ }
62
+ else {
63
+ for (const line of formatProjectTable(projects)) {
64
+ context.logger.info(line);
65
+ }
66
+ }
67
+ process.exitCode = 0;
68
+ }
69
+ catch (error) {
70
+ const message = error instanceof Error ? error.message : String(error);
71
+ if (context.json) {
72
+ context.logger.json({ status: 'error', message });
73
+ }
74
+ else {
75
+ context.logger.error(message);
76
+ }
77
+ process.exitCode = 1;
78
+ }
79
+ }
80
+ export function createProjectListCommand(overrides = {}) {
81
+ const dependencies = {
82
+ ...DEFAULT_DEPENDENCIES,
83
+ ...overrides,
84
+ };
85
+ return new Command('list')
86
+ .description('List tracked OAT projects')
87
+ .action(async (_options, command) => {
88
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
89
+ await runProjectList(context, dependencies);
90
+ });
91
+ }
@@ -0,0 +1,13 @@
1
+ import { type CommandContext, type GlobalOptions } from '../../app/command-context.js';
2
+ import { type ActiveProjectResolution } from '../../config/oat-config.js';
3
+ import { type ProjectState } from '@open-agent-toolkit/control-plane';
4
+ import { Command } from 'commander';
5
+ interface ProjectStatusDependencies {
6
+ buildCommandContext: (options: GlobalOptions) => CommandContext;
7
+ resolveProjectRoot: (cwd: string) => Promise<string>;
8
+ resolveActiveProject: (repoRoot: string) => Promise<ActiveProjectResolution>;
9
+ getProjectState: (projectPath: string) => Promise<ProjectState>;
10
+ }
11
+ export declare function createProjectStatusCommand(overrides?: Partial<ProjectStatusDependencies>): Command;
12
+ export {};
13
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/project/status.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC7E,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACjE;AAmFD,wBAAgB,0BAA0B,CACxC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAcT"}
@@ -0,0 +1,91 @@
1
+ import { join } from 'node:path';
2
+ import { buildCommandContext, } from '../../app/command-context.js';
3
+ import { readGlobalOptions } from '../shared/shared.utils.js';
4
+ import { resolveActiveProject, } from '../../config/oat-config.js';
5
+ import { resolveProjectRoot } from '../../fs/paths.js';
6
+ import { getProjectState, } from '@open-agent-toolkit/control-plane';
7
+ import { Command } from 'commander';
8
+ const DEFAULT_DEPENDENCIES = {
9
+ buildCommandContext,
10
+ resolveProjectRoot,
11
+ resolveActiveProject,
12
+ getProjectState,
13
+ };
14
+ function formatProjectStatusLines(project) {
15
+ return [
16
+ `Project: ${project.name}`,
17
+ `Path: ${project.path}`,
18
+ `Phase: ${project.phase} (${project.phaseStatus})`,
19
+ `Progress: ${project.progress.completed}/${project.progress.total}`,
20
+ `Current task: ${project.progress.currentTaskId ?? 'none'}`,
21
+ `Recommendation: ${project.recommendation.skill}`,
22
+ `Reason: ${project.recommendation.reason}`,
23
+ ];
24
+ }
25
+ async function runProjectStatus(context, dependencies) {
26
+ try {
27
+ const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
28
+ const activeProject = await dependencies.resolveActiveProject(repoRoot);
29
+ if (activeProject.status === 'unset') {
30
+ const message = 'No active project set (.oat/config.local.json has no activeProject).';
31
+ if (context.json) {
32
+ context.logger.json({ status: 'unset', message });
33
+ }
34
+ else {
35
+ context.logger.error(message);
36
+ }
37
+ process.exitCode = 1;
38
+ return;
39
+ }
40
+ if (activeProject.status === 'missing' || !activeProject.path) {
41
+ const message = activeProject.path
42
+ ? `Active project path is missing or invalid: ${activeProject.path}`
43
+ : 'Active project path is missing or invalid.';
44
+ if (context.json) {
45
+ context.logger.json({
46
+ status: 'missing',
47
+ projectName: activeProject.name,
48
+ projectPath: activeProject.path,
49
+ message,
50
+ });
51
+ }
52
+ else {
53
+ context.logger.error(message);
54
+ }
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ const project = await dependencies.getProjectState(join(repoRoot, activeProject.path));
59
+ if (context.json) {
60
+ context.logger.json({ status: 'ok', project });
61
+ }
62
+ else {
63
+ for (const line of formatProjectStatusLines(project)) {
64
+ context.logger.info(line);
65
+ }
66
+ }
67
+ process.exitCode = 0;
68
+ }
69
+ catch (error) {
70
+ const message = error instanceof Error ? error.message : String(error);
71
+ if (context.json) {
72
+ context.logger.json({ status: 'error', message });
73
+ }
74
+ else {
75
+ context.logger.error(message);
76
+ }
77
+ process.exitCode = 1;
78
+ }
79
+ }
80
+ export function createProjectStatusCommand(overrides = {}) {
81
+ const dependencies = {
82
+ ...DEFAULT_DEPENDENCIES,
83
+ ...overrides,
84
+ };
85
+ return new Command('status')
86
+ .description('Show the current OAT project state')
87
+ .action(async (_options, command) => {
88
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
89
+ await runProjectStatus(context, dependencies);
90
+ });
91
+ }
@@ -0,0 +1,19 @@
1
+ import { type OatConfig, type OatLocalConfig, type UserConfig } from './oat-config.js';
2
+ export type ResolvedConfigSource = 'shared' | 'local' | 'user' | 'env' | 'default';
3
+ export interface ResolvedKeyEntry {
4
+ value: unknown;
5
+ source: ResolvedConfigSource;
6
+ }
7
+ export interface ResolvedConfig {
8
+ shared: OatConfig;
9
+ local: OatLocalConfig;
10
+ user: UserConfig;
11
+ resolved: Record<string, ResolvedKeyEntry>;
12
+ }
13
+ export interface ResolveEffectiveConfigDependencies {
14
+ readOatConfig: (repoRoot: string) => Promise<OatConfig>;
15
+ readOatLocalConfig: (repoRoot: string) => Promise<OatLocalConfig>;
16
+ readUserConfig: (userConfigDir: string) => Promise<UserConfig>;
17
+ }
18
+ export declare function resolveEffectiveConfig(repoRoot: string, userConfigDir: string, env?: NodeJS.ProcessEnv, overrides?: Partial<ResolveEffectiveConfigDependencies>): Promise<ResolvedConfig>;
19
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/config/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,oBAAoB,GAC5B,QAAQ,GACR,OAAO,GACP,MAAM,GACN,KAAK,GACL,SAAS,CAAC;AAEd,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,kCAAkC;IACjD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CAChE;AA0CD,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,SAAS,GAAE,OAAO,CAAC,kCAAkC,CAAM,GAC1D,OAAO,CAAC,cAAc,CAAC,CAoEzB"}
@@ -0,0 +1,128 @@
1
+ import { readOatConfig, readOatLocalConfig, readUserConfig, } from './oat-config.js';
2
+ const DEFAULT_DEPENDENCIES = {
3
+ readOatConfig,
4
+ readOatLocalConfig,
5
+ readUserConfig,
6
+ };
7
+ const DEFAULT_SHARED_CONFIG = {
8
+ projects: { root: '.oat/projects/shared' },
9
+ worktrees: { root: '.worktrees' },
10
+ git: { defaultBranch: 'main' },
11
+ archive: {
12
+ s3Uri: null,
13
+ s3SyncOnComplete: false,
14
+ summaryExportPath: null,
15
+ },
16
+ documentation: {
17
+ root: null,
18
+ tooling: null,
19
+ config: null,
20
+ index: null,
21
+ requireForProjectCompletion: false,
22
+ },
23
+ autoReviewAtCheckpoints: false,
24
+ };
25
+ const DEFAULT_LOCAL_CONFIG = {
26
+ activeProject: null,
27
+ lastPausedProject: null,
28
+ activeIdea: null,
29
+ };
30
+ const DEFAULT_USER_CONFIG = {
31
+ activeIdea: null,
32
+ };
33
+ const ENV_OVERRIDE_MAP = {
34
+ 'projects.root': 'OAT_PROJECTS_ROOT',
35
+ 'worktrees.root': 'OAT_WORKTREES_ROOT',
36
+ };
37
+ export async function resolveEffectiveConfig(repoRoot, userConfigDir, env = process.env, overrides = {}) {
38
+ const dependencies = {
39
+ ...DEFAULT_DEPENDENCIES,
40
+ ...overrides,
41
+ };
42
+ const [shared, local, user] = await Promise.all([
43
+ dependencies.readOatConfig(repoRoot),
44
+ dependencies.readOatLocalConfig(repoRoot),
45
+ dependencies.readUserConfig(userConfigDir),
46
+ ]);
47
+ const sharedValues = flattenConfig(shared);
48
+ const localValues = flattenConfig(local);
49
+ const userValues = flattenConfig(user);
50
+ const defaultValues = {
51
+ ...flattenConfig(DEFAULT_SHARED_CONFIG),
52
+ ...flattenConfig(DEFAULT_LOCAL_CONFIG),
53
+ ...flattenConfig(DEFAULT_USER_CONFIG),
54
+ };
55
+ const keys = new Set([
56
+ ...Object.keys(defaultValues),
57
+ ...Object.keys(sharedValues),
58
+ ...Object.keys(localValues),
59
+ ...Object.keys(userValues),
60
+ ]);
61
+ const resolved = {};
62
+ for (const key of [...keys].sort()) {
63
+ const envValue = resolveEnvOverride(key, env);
64
+ if (envValue !== undefined) {
65
+ resolved[key] = { value: envValue, source: 'env' };
66
+ continue;
67
+ }
68
+ const localValue = localValues[key];
69
+ if (isResolvedValue(localValue)) {
70
+ resolved[key] = { value: localValue, source: 'local' };
71
+ continue;
72
+ }
73
+ const sharedValue = sharedValues[key];
74
+ if (isResolvedValue(sharedValue)) {
75
+ resolved[key] = { value: sharedValue, source: 'shared' };
76
+ continue;
77
+ }
78
+ const userValue = userValues[key];
79
+ if (isResolvedValue(userValue)) {
80
+ resolved[key] = { value: userValue, source: 'user' };
81
+ continue;
82
+ }
83
+ if (key in defaultValues) {
84
+ resolved[key] = {
85
+ value: defaultValues[key] ?? null,
86
+ source: 'default',
87
+ };
88
+ }
89
+ }
90
+ return {
91
+ shared,
92
+ local,
93
+ user,
94
+ resolved,
95
+ };
96
+ }
97
+ function flattenConfig(value, prefix = '') {
98
+ if (!isRecord(value)) {
99
+ return {};
100
+ }
101
+ const flattened = {};
102
+ for (const [key, nestedValue] of Object.entries(value)) {
103
+ if (key === 'version') {
104
+ continue;
105
+ }
106
+ const nextKey = prefix ? `${prefix}.${key}` : key;
107
+ if (isRecord(nestedValue)) {
108
+ Object.assign(flattened, flattenConfig(nestedValue, nextKey));
109
+ continue;
110
+ }
111
+ flattened[nextKey] = nestedValue;
112
+ }
113
+ return flattened;
114
+ }
115
+ function resolveEnvOverride(key, env) {
116
+ const envKey = ENV_OVERRIDE_MAP[key];
117
+ if (!envKey) {
118
+ return undefined;
119
+ }
120
+ const value = env[envKey]?.trim();
121
+ return value ? value.replace(/\/+$/, '') : undefined;
122
+ }
123
+ function isRecord(value) {
124
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
125
+ }
126
+ function isResolvedValue(value) {
127
+ return value !== undefined && value !== null;
128
+ }
@@ -3,7 +3,7 @@ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
3
3
  import { dirname } from 'node:path';
4
4
  import { CliError } from '../errors/index.js';
5
5
  import { ManifestSchema, } from './manifest.types.js';
6
- const OAT_VERSION = '0.0.18';
6
+ const OAT_VERSION = '0.0.24';
7
7
  function formatIssuePath(path) {
8
8
  if (path.length === 0) {
9
9
  return '(root)';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/cli",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "private": false,
5
5
  "description": "Open Agent Toolkit CLI",
6
6
  "homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
@@ -32,7 +32,8 @@
32
32
  "commander": "^12.1.0",
33
33
  "ora": "^9.0.0",
34
34
  "yaml": "2.8.2",
35
- "zod": "^3.25.76"
35
+ "zod": "^3.25.76",
36
+ "@open-agent-toolkit/control-plane": "0.0.1"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@types/node": "^22.10.0",