thoth-agents 0.3.5 → 0.3.6

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/dist/cli/index.js CHANGED
@@ -42,8 +42,9 @@ import {
42
42
  getRequiredSkillInstallCommand,
43
43
  installRequiredSkill,
44
44
  listOperationHarnesses,
45
- normalizeEffortSelection
46
- } from "../chunk-W5GCMYA3.js";
45
+ normalizeEffortSelection,
46
+ syncOpenCodeOwnedSkills
47
+ } from "../chunk-HIXSRHEG.js";
47
48
  import {
48
49
  ALL_AGENT_NAMES,
49
50
  detectCurrentConfig,
@@ -551,9 +552,30 @@ function formatConfigSummary(dryRun) {
551
552
  lines.push(
552
553
  ` ${DIM}\u25CB thoth-mem remains the owner of hooks, MCP, skill, lifecycle, persistence, receipts, and recovery.${RESET}`
553
554
  );
555
+ lines.push(` ${SYMBOLS.check} Global thoth-owned OpenCode skills`);
554
556
  lines.push(` ${SYMBOLS.check} Required external skills for this harness`);
555
557
  return lines.join("\n");
556
558
  }
559
+ function installOwnedSkillsForOpenCode(dryRun, dependencies) {
560
+ const result = syncOpenCodeOwnedSkills({
561
+ dryRun,
562
+ homeDir: dependencies.homeDir ?? homedir(),
563
+ packageRoot: dependencies.opencodeOwnedSkillPackageRoot
564
+ });
565
+ for (const skill of result.skills) {
566
+ printInfo(` - ${skill.name}: ${skill.destinationPath}`);
567
+ }
568
+ if (!result.success) {
569
+ printError(
570
+ `Failed to synchronize global thoth-owned OpenCode skills: ${result.error ?? "unknown error"}`
571
+ );
572
+ return false;
573
+ }
574
+ printSuccess(
575
+ dryRun ? "Global thoth-owned OpenCode skills planned" : "Global thoth-owned OpenCode skills synchronized"
576
+ );
577
+ return true;
578
+ }
557
579
  function installRequiredSkillsForHarness(harness, dryRun, homeDir = homedir()) {
558
580
  if (dryRun) {
559
581
  printInfo(`Dry run mode - required ${harness} skills:`);
@@ -617,7 +639,7 @@ async function runInstall(config, dependencies) {
617
639
  const detected = detectCurrentConfig();
618
640
  const isUpdate = detected.isInstalled;
619
641
  printHeader(isUpdate);
620
- const totalSteps = 5;
642
+ const totalSteps = 6;
621
643
  let step = 1;
622
644
  printStep(step++, totalSteps, "Checking OpenCode installation...");
623
645
  if (config.dryRun) {
@@ -666,8 +688,19 @@ ${JSON.stringify(liteConfig, null, 2)}
666
688
  return 1;
667
689
  }
668
690
  }
691
+ printStep(
692
+ step++,
693
+ totalSteps,
694
+ "Synchronizing global thoth-owned OpenCode skills..."
695
+ );
696
+ if (!installOwnedSkillsForOpenCode(config.dryRun, dependencies)) return 1;
669
697
  printStep(step++, totalSteps, "Installing required external skills...");
670
- if (!installRequiredSkillsForHarness("opencode", config.dryRun)) return 1;
698
+ if (!installRequiredSkillsForHarness(
699
+ "opencode",
700
+ config.dryRun,
701
+ dependencies.homeDir ?? homedir()
702
+ ))
703
+ return 1;
671
704
  printStep(step++, totalSteps, "Configuring provider-owned thoth-mem...");
672
705
  if (!installThothMemForHarness("opencode", config.dryRun, dependencies)) {
673
706
  return 1;
@@ -1,6 +1,8 @@
1
1
  import { type ThothMemSetupOptions, type ThothMemSetupResult } from './thoth-mem-install';
2
2
  import type { InstallArgs, InstallConfig } from './types';
3
3
  export interface InstallDependencies {
4
+ homeDir?: string;
5
+ opencodeOwnedSkillPackageRoot?: string;
4
6
  runThothMemSetup?: (options: ThothMemSetupOptions) => ThothMemSetupResult;
5
7
  }
6
8
  export declare function createInstallConfig(args: InstallArgs): InstallConfig;
@@ -9,7 +9,7 @@ export declare const opencodeOperationAdapter: {
9
9
  };
10
10
  export declare function getOpenCodeStatus(context?: OperationContext, evidence?: ProviderEvidenceInput): HarnessStatusReport;
11
11
  export declare function buildOpenCodeUpdatePlan(_context?: OperationContext): OperationPlan;
12
- export declare function buildOpenCodeSyncPlan(_context?: OperationContext): OperationPlan;
13
- export declare function buildOpenCodeInstallPlan(_context?: OperationContext): OperationPlan;
12
+ export declare function buildOpenCodeSyncPlan(context?: OperationContext): OperationPlan;
13
+ export declare function buildOpenCodeInstallPlan(context?: OperationContext): OperationPlan;
14
14
  export declare function buildOpenCodeModelPlan(input: ModelConfigInput, _context?: OperationContext): OperationPlan;
15
15
  export declare function applyOpenCodePlan(plan: OperationPlan): OperationApplyResult;
@@ -0,0 +1,20 @@
1
+ import { type ThothOwnedSkillName } from '../harness/core/owned-skills';
2
+ export interface OpenCodeOwnedSkillSyncOptions {
3
+ dryRun?: boolean;
4
+ homeDir?: string;
5
+ packageRoot?: string;
6
+ }
7
+ export interface OpenCodeOwnedSkillSyncEntry {
8
+ name: ThothOwnedSkillName;
9
+ sourcePath: string;
10
+ destinationPath: string;
11
+ }
12
+ export interface OpenCodeOwnedSkillSyncResult {
13
+ success: boolean;
14
+ status: 'planned' | 'installed' | 'failed';
15
+ skills: OpenCodeOwnedSkillSyncEntry[];
16
+ error?: string;
17
+ }
18
+ export declare function resolveOwnedSkillPackageRoot(moduleUrl: string): string;
19
+ export declare function getOpenCodeOwnedSkillEntries(options?: Pick<OpenCodeOwnedSkillSyncOptions, 'homeDir' | 'packageRoot'>): OpenCodeOwnedSkillSyncEntry[];
20
+ export declare function syncOpenCodeOwnedSkills(options?: OpenCodeOwnedSkillSyncOptions): OpenCodeOwnedSkillSyncResult;
@@ -2,7 +2,7 @@ import {
2
2
  defaultTuiOperations,
3
3
  effortChoicesForModel,
4
4
  listOperationHarnesses
5
- } from "../../chunk-W5GCMYA3.js";
5
+ } from "../../chunk-HIXSRHEG.js";
6
6
  import "../../chunk-F2K4CS5J.js";
7
7
 
8
8
  // src/cli/tui/index.tsx
@@ -0,0 +1,2 @@
1
+ export declare const THOTH_OWNED_SKILL_NAMES: readonly ["thoth-init", "thoth-sdd", "thoth-constitution", "thoth-archive", "plan-reviewer"];
2
+ export type ThothOwnedSkillName = (typeof THOTH_OWNED_SKILL_NAMES)[number];
package/dist/index.js CHANGED
@@ -42,12 +42,13 @@ function createOpenCodeInitCommand({
42
42
  subtask: false,
43
43
  template: `Initialize this project now using the bundled thoth-init skill.
44
44
 
45
- The operation must stay project-scoped, idempotent, and offline. Run:
45
+ The operation must stay scoped to openspec/, idempotent, and offline. Run:
46
46
 
47
- node "${scriptPath}" --project "${projectRoot}" --harness opencode --json
47
+ node "${scriptPath}" --project "${projectRoot}" --json
48
48
 
49
- Inspect the JSON result, report created and preserved files, and do not download
50
- skills or invoke the thoth-agents CLI.
49
+ Inspect the JSON result and report created, managed, and preserved OpenSpec
50
+ assets. Do not install skills or agents, download dependencies, modify harness
51
+ configuration, or invoke the thoth-agents CLI.
51
52
 
52
53
  Bundled skill contract:
53
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Adaptive multi-harness orchestration with seven roles, runtime-autonomous Spec Kit-compatible SDD routes, and external provider boundaries.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Adaptive multi-harness agent pack with seven roles and a runtime-autonomous Spec Kit-compatible SDD bundle for Claude Code.",
5
5
  "author": {
6
6
  "name": "thoth-agents"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Bundled Spec Kit-compatible SDD skills and MCP configuration; the seven-role global Codex layer requires thoth-agents CLI setup.",
5
5
  "skills": "./skills/",
6
6
  "mcpServers": "./codex.mcp.json"
@@ -1,30 +1,33 @@
1
1
  ---
2
2
  name: thoth-init
3
- description: Initialize project SDD governance and OpenCode local skills from the installed bundle; Codex global agents and instructions remain CLI-owned.
3
+ description: Initialize or synchronize the minimum project OpenSpec governance structure required by thoth-agents SDD flows.
4
4
  ---
5
5
 
6
6
  # Thoth Init
7
7
 
8
- Initialize the current project from this installed skill bundle. Resolve
9
- `<skill-dir>` as the directory containing this `SKILL.md`, then run the bundled
10
- script by absolute path:
8
+ Initialize or synchronize the current project's OpenSpec governance from this
9
+ installed skill bundle. Resolve `<skill-dir>` as the directory containing this
10
+ `SKILL.md`, then run the bundled script by absolute path:
11
11
 
12
12
  ```text
13
- node "<skill-dir>/scripts/init.mjs" --project <project-root> --harness <opencode|codex|claude> --json
13
+ node "<skill-dir>/scripts/init.mjs" --project <project-root> --json
14
14
  ```
15
15
 
16
- - OpenCode materializes the five thoth-owned workflow skills under
17
- `.agents/skills/`.
18
- - Codex init creates project governance only. Codex plugin manifests cannot
19
- install custom agents or global instructions; first run the mandatory
20
- thoth-agents CLI setup that manages `~/.codex/agents/`,
21
- `~/.codex/AGENTS.md`, and `~/.codex/config.toml`.
22
- - Claude uses auto-discovered plugin agents and owned skills; init creates only
23
- project governance assets.
16
+ The project root must already exist. The initializer preflights the complete
17
+ target structure before changing it, then ensures these minimum paths exist:
24
18
 
25
- For every harness, installation must already have used the thoth-agents CLI to
26
- install mandatory external skills from their canonical repositories. This init
27
- skill never invokes the CLI or downloads a skill.
19
+ - `openspec/changes/archive/`
20
+ - `openspec/specs/`
21
+ - `openspec/memory/`
22
+ - `openspec/templates/`
23
+ - `openspec/.thoth-agents.json`
28
24
 
29
- The operation is offline and idempotent. It never overwrites the project
30
- constitution, templates, or unrelated instruction content.
25
+ Missing constitution and SDD template files are copied from the installed
26
+ bundle. Existing constitution and template files remain byte-for-byte intact;
27
+ the thoth-managed manifest may be normalized to the current contract. Inspect
28
+ the JSON `created`, `managed`, and `preserved` arrays when reporting the result.
29
+
30
+ This operation is offline, idempotent, and harness-neutral. Every write stays
31
+ inside `openspec/`. It never installs or synchronizes skills, agents, plugins,
32
+ harness configuration, external dependencies, or global instruction files;
33
+ those are responsibilities of `npx thoth-agents install`.
@@ -3,10 +3,10 @@
3
3
  import {
4
4
  cpSync,
5
5
  existsSync,
6
+ lstatSync,
6
7
  mkdirSync,
7
8
  readdirSync,
8
9
  readFileSync,
9
- statSync,
10
10
  writeFileSync,
11
11
  } from 'node:fs';
12
12
  import { dirname, join, resolve } from 'node:path';
@@ -15,12 +15,13 @@ import { fileURLToPath } from 'node:url';
15
15
  const SCRIPT_PATH = fileURLToPath(import.meta.url);
16
16
  const SKILL_ROOT = dirname(dirname(SCRIPT_PATH));
17
17
  const BUNDLE_ROOT = dirname(SKILL_ROOT);
18
- const OWNED_SKILL_NAMES = [
19
- 'thoth-init',
20
- 'thoth-sdd',
21
- 'thoth-constitution',
22
- 'thoth-archive',
23
- 'plan-reviewer',
18
+ const OPEN_SPEC_DIRECTORIES = [
19
+ 'openspec',
20
+ join('openspec', 'changes'),
21
+ join('openspec', 'changes', 'archive'),
22
+ join('openspec', 'specs'),
23
+ join('openspec', 'memory'),
24
+ join('openspec', 'templates'),
24
25
  ];
25
26
 
26
27
  function parseArgs(argv) {
@@ -29,110 +30,193 @@ function parseArgs(argv) {
29
30
  const argument = argv[index];
30
31
  if (argument === '--json') options.json = true;
31
32
  else if (argument === '--project') options.project = argv[++index];
32
- else if (argument === '--harness') options.harness = argv[++index];
33
33
  else throw new Error(`Unknown argument: ${argument}`);
34
34
  }
35
35
  if (!options.project) throw new Error('--project is required');
36
- if (!['opencode', 'codex', 'claude'].includes(options.harness)) {
37
- throw new Error('--harness must be opencode, codex, or claude');
38
- }
39
36
  return options;
40
37
  }
41
38
 
42
- function copyMissing(source, target, report) {
43
- if (!existsSync(source)) return;
44
- const sourceStat = statSync(source);
45
- if (sourceStat.isDirectory()) {
46
- mkdirSync(target, { recursive: true });
47
- for (const entry of readdirSync(source)) {
48
- copyMissing(join(source, entry), join(target, entry), report);
39
+ function assertDirectory(path, label) {
40
+ if (!existsSync(path)) return;
41
+ const stat = lstatSync(path);
42
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
43
+ throw new Error(`${label} must be a directory: ${path}`);
44
+ }
45
+ }
46
+
47
+ function assertRegularFile(path, label) {
48
+ if (!existsSync(path)) return;
49
+ const stat = lstatSync(path);
50
+ if (stat.isSymbolicLink() || !stat.isFile()) {
51
+ throw new Error(`${label} must be a regular file: ${path}`);
52
+ }
53
+ }
54
+
55
+ function collectFiles(sourceRoot, targetRoot) {
56
+ if (!existsSync(sourceRoot)) {
57
+ throw new Error(`Bundled SDD template directory is missing: ${sourceRoot}`);
58
+ }
59
+ assertDirectory(sourceRoot, 'Bundled template directory');
60
+ const entries = [];
61
+
62
+ function visit(source, target) {
63
+ for (const entry of readdirSync(source, { withFileTypes: true })) {
64
+ const sourcePath = join(source, entry.name);
65
+ const targetPath = join(target, entry.name);
66
+ if (entry.isSymbolicLink()) {
67
+ throw new Error(
68
+ `Bundled templates cannot contain symlinks: ${sourcePath}`,
69
+ );
70
+ }
71
+ if (entry.isDirectory()) {
72
+ entries.push({ kind: 'directory', target: targetPath });
73
+ visit(sourcePath, targetPath);
74
+ } else if (entry.isFile()) {
75
+ entries.push({ kind: 'file', source: sourcePath, target: targetPath });
76
+ } else {
77
+ throw new Error(`Unsupported bundled template entry: ${sourcePath}`);
78
+ }
49
79
  }
50
- return;
51
80
  }
81
+
82
+ visit(sourceRoot, targetRoot);
83
+ return entries;
84
+ }
85
+
86
+ function preflight(project) {
87
+ assertDirectory(project, 'Project root');
88
+ if (!existsSync(project)) {
89
+ throw new Error(
90
+ `--project must reference an existing project directory: ${project}`,
91
+ );
92
+ }
93
+
94
+ const constitutionSource = join(
95
+ BUNDLE_ROOT,
96
+ 'thoth-constitution',
97
+ 'templates',
98
+ 'constitution.md',
99
+ );
100
+ assertRegularFile(constitutionSource, 'Bundled constitution template');
101
+ if (!existsSync(constitutionSource)) {
102
+ throw new Error(
103
+ `Bundled constitution template is missing: ${constitutionSource}`,
104
+ );
105
+ }
106
+
107
+ const templates = collectFiles(
108
+ join(BUNDLE_ROOT, 'thoth-sdd', 'templates'),
109
+ join(project, 'openspec', 'templates'),
110
+ );
111
+ for (const directory of OPEN_SPEC_DIRECTORIES) {
112
+ assertDirectory(join(project, directory), 'OpenSpec path');
113
+ }
114
+ for (const entry of templates) {
115
+ if (entry.kind === 'directory') {
116
+ assertDirectory(entry.target, 'OpenSpec template path');
117
+ } else {
118
+ assertRegularFile(entry.target, 'OpenSpec template path');
119
+ }
120
+ }
121
+
122
+ const constitutionTarget = join(
123
+ project,
124
+ 'openspec',
125
+ 'memory',
126
+ 'constitution.md',
127
+ );
128
+ const manifestTarget = join(project, 'openspec', '.thoth-agents.json');
129
+ assertRegularFile(constitutionTarget, 'OpenSpec constitution path');
130
+ assertRegularFile(manifestTarget, 'OpenSpec manifest path');
131
+
132
+ return {
133
+ constitutionSource,
134
+ constitutionTarget,
135
+ manifestTarget,
136
+ templates,
137
+ };
138
+ }
139
+
140
+ function createDirectory(target, report) {
141
+ if (existsSync(target)) return;
142
+ mkdirSync(target);
143
+ report.created.push(target);
144
+ }
145
+
146
+ function copyPreservingExisting(source, target, report) {
52
147
  if (existsSync(target)) {
53
- report.skipped.push(target);
148
+ report.preserved.push(target);
54
149
  return;
55
150
  }
56
- mkdirSync(dirname(target), { recursive: true });
57
151
  cpSync(source, target, { errorOnExist: true });
58
152
  report.created.push(target);
59
153
  }
60
154
 
61
- function writeMissing(target, content, report) {
155
+ function writePreservingExisting(target, content, report) {
62
156
  if (existsSync(target)) {
63
- report.skipped.push(target);
157
+ report.preserved.push(target);
64
158
  return;
65
159
  }
66
- mkdirSync(dirname(target), { recursive: true });
67
160
  writeFileSync(target, content);
68
161
  report.created.push(target);
69
162
  }
70
163
 
71
- function installConstitution(project, report) {
72
- const target = join(project, 'openspec', 'memory', 'constitution.md');
73
- const template = join(
74
- BUNDLE_ROOT,
75
- 'thoth-constitution',
76
- 'templates',
77
- 'constitution.md',
78
- );
79
- if (!existsSync(template)) return;
80
- const today = new Date().toISOString().slice(0, 10);
81
- writeMissing(
82
- target,
83
- readFileSync(template, 'utf8').replaceAll('YYYY-MM-DD', today),
84
- report,
85
- );
164
+ function synchronizeManagedFile(target, content, report) {
165
+ if (!existsSync(target)) {
166
+ writeFileSync(target, content);
167
+ report.created.push(target);
168
+ return;
169
+ }
170
+ if (readFileSync(target, 'utf8') === content) {
171
+ report.preserved.push(target);
172
+ return;
173
+ }
174
+ writeFileSync(target, content);
175
+ report.managed.push(target);
86
176
  }
87
177
 
88
- function installGovernance(project, report) {
89
- installConstitution(project, report);
90
- const templatesRoot = join(BUNDLE_ROOT, 'thoth-sdd', 'templates');
91
- if (existsSync(templatesRoot)) {
92
- for (const entry of readdirSync(templatesRoot)) {
93
- copyMissing(
94
- join(templatesRoot, entry),
95
- join(project, 'openspec', 'templates', entry),
96
- report,
97
- );
98
- }
178
+ function synchronizeOpenSpec(project, assets, report) {
179
+ for (const directory of OPEN_SPEC_DIRECTORIES) {
180
+ createDirectory(join(project, directory), report);
99
181
  }
100
- writeMissing(
101
- join(project, 'openspec', '.thoth-agents.json'),
182
+ for (const entry of assets.templates) {
183
+ if (entry.kind === 'directory') createDirectory(entry.target, report);
184
+ else copyPreservingExisting(entry.source, entry.target, report);
185
+ }
186
+
187
+ const today = new Date().toISOString().slice(0, 10);
188
+ writePreservingExisting(
189
+ assets.constitutionTarget,
190
+ readFileSync(assets.constitutionSource, 'utf8').replaceAll(
191
+ 'YYYY-MM-DD',
192
+ today,
193
+ ),
194
+ report,
195
+ );
196
+ synchronizeManagedFile(
197
+ assets.manifestTarget,
102
198
  `${JSON.stringify({ version: 1, initializedBy: 'thoth-agents' }, null, 2)}\n`,
103
199
  report,
104
200
  );
105
201
  }
106
202
 
107
- function installOpenCode(project, report) {
108
- for (const skillName of OWNED_SKILL_NAMES) {
109
- copyMissing(
110
- join(BUNDLE_ROOT, skillName),
111
- join(project, '.agents', 'skills', skillName),
112
- report,
113
- );
114
- }
115
- }
116
-
117
203
  try {
118
204
  const options = parseArgs(process.argv.slice(2));
119
205
  const project = resolve(options.project);
120
- mkdirSync(project, { recursive: true });
206
+ const assets = preflight(project);
121
207
  const report = {
122
208
  status: 'ready',
123
- harness: options.harness,
124
209
  project,
125
210
  created: [],
126
211
  managed: [],
127
- skipped: [],
212
+ preserved: [],
128
213
  };
129
214
 
130
- installGovernance(project, report);
131
- if (options.harness === 'opencode') installOpenCode(project, report);
215
+ synchronizeOpenSpec(project, assets, report);
132
216
 
133
217
  const output = options.json
134
218
  ? JSON.stringify(report)
135
- : `thoth-agents initialized ${project} for ${options.harness}`;
219
+ : `thoth-agents synchronized OpenSpec governance in ${project}`;
136
220
  process.stdout.write(`${output}\n`);
137
221
  } catch (error) {
138
222
  process.stderr.write(
package/skills/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  This directory is the canonical, versioned source for skills owned by
4
4
  thoth-agents. `pnpm run integration:sync` copies those skills into the Codex and
5
- Claude Code plugins; OpenCode exposes `/thoth-init`, which materializes them
6
- project-locally.
5
+ Claude Code plugins; `npx thoth-agents install --agent=opencode` materializes
6
+ the same packaged skill trees globally under `~/.config/opencode/skills/`.
7
7
 
8
8
  Owned workflow skills are `thoth-init`, `thoth-sdd`, `thoth-constitution`,
9
9
  `thoth-archive`, and `plan-reviewer`. The last one implements the optional,
@@ -11,4 +11,6 @@ user-selected, read-only Oracle review before implementation. Mandatory external
11
11
  skills (`simplify`, `tdd`,
12
12
  `progressive-context-router`, and `architectural-grilling`) are not copied here:
13
13
  the thoth-agents installer invokes `npx skills add` against their canonical
14
- repositories so they retain a single source of truth.
14
+ repositories so they retain a single source of truth. `thoth-init` only
15
+ initializes or synchronizes minimum project `openspec/` governance and never
16
+ installs skills.
@@ -1,30 +1,33 @@
1
1
  ---
2
2
  name: thoth-init
3
- description: Initialize project SDD governance and OpenCode local skills from the installed bundle; Codex global agents and instructions remain CLI-owned.
3
+ description: Initialize or synchronize the minimum project OpenSpec governance structure required by thoth-agents SDD flows.
4
4
  ---
5
5
 
6
6
  # Thoth Init
7
7
 
8
- Initialize the current project from this installed skill bundle. Resolve
9
- `<skill-dir>` as the directory containing this `SKILL.md`, then run the bundled
10
- script by absolute path:
8
+ Initialize or synchronize the current project's OpenSpec governance from this
9
+ installed skill bundle. Resolve `<skill-dir>` as the directory containing this
10
+ `SKILL.md`, then run the bundled script by absolute path:
11
11
 
12
12
  ```text
13
- node "<skill-dir>/scripts/init.mjs" --project <project-root> --harness <opencode|codex|claude> --json
13
+ node "<skill-dir>/scripts/init.mjs" --project <project-root> --json
14
14
  ```
15
15
 
16
- - OpenCode materializes the five thoth-owned workflow skills under
17
- `.agents/skills/`.
18
- - Codex init creates project governance only. Codex plugin manifests cannot
19
- install custom agents or global instructions; first run the mandatory
20
- thoth-agents CLI setup that manages `~/.codex/agents/`,
21
- `~/.codex/AGENTS.md`, and `~/.codex/config.toml`.
22
- - Claude uses auto-discovered plugin agents and owned skills; init creates only
23
- project governance assets.
16
+ The project root must already exist. The initializer preflights the complete
17
+ target structure before changing it, then ensures these minimum paths exist:
24
18
 
25
- For every harness, installation must already have used the thoth-agents CLI to
26
- install mandatory external skills from their canonical repositories. This init
27
- skill never invokes the CLI or downloads a skill.
19
+ - `openspec/changes/archive/`
20
+ - `openspec/specs/`
21
+ - `openspec/memory/`
22
+ - `openspec/templates/`
23
+ - `openspec/.thoth-agents.json`
28
24
 
29
- The operation is offline and idempotent. It never overwrites the project
30
- constitution, templates, or unrelated instruction content.
25
+ Missing constitution and SDD template files are copied from the installed
26
+ bundle. Existing constitution and template files remain byte-for-byte intact;
27
+ the thoth-managed manifest may be normalized to the current contract. Inspect
28
+ the JSON `created`, `managed`, and `preserved` arrays when reporting the result.
29
+
30
+ This operation is offline, idempotent, and harness-neutral. Every write stays
31
+ inside `openspec/`. It never installs or synchronizes skills, agents, plugins,
32
+ harness configuration, external dependencies, or global instruction files;
33
+ those are responsibilities of `npx thoth-agents install`.