@aiwg/cli 2026.9.4 → 2026.9.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.
Files changed (93) hide show
  1. package/THIRD_PARTY_NOTICES.md +16 -0
  2. package/agentic/code/providers/capability-matrix.yaml +45 -3
  3. package/agentic/code/providers/deepseek-harness/README.md +8 -0
  4. package/agentic/code/providers/deepseek-harness/aiwg.cordis.patch.yml +10 -0
  5. package/agentic/code/providers/model-capabilities.v1.json +11 -0
  6. package/agentic/code/providers/model-catalog.v1.json +8 -0
  7. package/bin/aiwg.mjs +1 -0
  8. package/dist/src/api/index.d.ts +14 -0
  9. package/dist/src/api/index.js +14 -0
  10. package/dist/src/artifacts/corpus-tools/source-types.js +1 -0
  11. package/dist/src/artifacts/index-files.js +17 -2
  12. package/dist/src/artifacts/repair.js +55 -6
  13. package/dist/src/catalog/cli.js +21 -7
  14. package/dist/src/catalog/cli.mjs +22 -7
  15. package/dist/src/cli/agent-spawn.js +10 -1
  16. package/dist/src/cli/handlers/artifacts.js +22 -3
  17. package/dist/src/cli/handlers/help.js +3 -0
  18. package/dist/src/cli/handlers/index.js +5 -1
  19. package/dist/src/cli/handlers/models.js +2 -2
  20. package/dist/src/cli/handlers/output-mode.js +1 -1
  21. package/dist/src/cli/handlers/runtime-info.js +1 -1
  22. package/dist/src/cli/handlers/sessions.js +55 -15
  23. package/dist/src/cli/handlers/steward.js +1 -1
  24. package/dist/src/cli/handlers/subcommands.js +5 -0
  25. package/dist/src/cli/handlers/writer-profile.js +110 -0
  26. package/dist/src/cli/handlers/writing.js +122 -0
  27. package/dist/src/cli/router.js +5 -1
  28. package/dist/src/config/project-artifacts-runtime.mjs +33 -1
  29. package/dist/src/config/project-artifacts.js +1 -1
  30. package/dist/src/dataset/fortemi-dataset-execution.d.ts +23 -0
  31. package/dist/src/dataset/fortemi-dataset-execution.js +158 -0
  32. package/dist/src/dataset/fortemi-live-qualification.d.ts +4 -2
  33. package/dist/src/dataset/fortemi-live-qualification.js +20 -21
  34. package/dist/src/dataset/fortemi-run-receipt.d.ts +44 -0
  35. package/dist/src/dataset/fortemi-run-receipt.js +74 -0
  36. package/dist/src/dataset/index.d.ts +2 -0
  37. package/dist/src/dataset/index.js +2 -0
  38. package/dist/src/extensions/commands/definitions.js +24 -0
  39. package/dist/src/extensions/manifest.js +3 -0
  40. package/dist/src/mcp/server.mjs +2 -0
  41. package/dist/src/mcp/tools/writer-profiles.mjs +40 -0
  42. package/dist/src/models/model-capabilities.v1.json +11 -0
  43. package/dist/src/models/model-catalog.v1.json +8 -0
  44. package/dist/src/models/provider-policy.js +1 -1
  45. package/dist/src/network-analysis/analyzer.js +667 -0
  46. package/dist/src/network-analysis/citations.js +107 -0
  47. package/dist/src/network-analysis/forensics.js +132 -0
  48. package/dist/src/network-analysis/governance.js +216 -0
  49. package/dist/src/network-analysis/index.js +10 -0
  50. package/dist/src/network-analysis/probe.js +405 -0
  51. package/dist/src/network-analysis/recipes.js +88 -0
  52. package/dist/src/network-analysis/research.js +181 -0
  53. package/dist/src/network-analysis/termshark.js +252 -0
  54. package/dist/src/network-analysis/verification.js +171 -0
  55. package/dist/src/output-modes/registry.js +37 -6
  56. package/dist/src/output-modes/runtime.js +164 -28
  57. package/dist/src/providers/capability-matrix.yaml +45 -3
  58. package/dist/src/providers/provider-definitions.js +49 -0
  59. package/dist/src/providers/provider-inventory.js +1 -0
  60. package/dist/src/providers/transformation-receipt.js +3 -2
  61. package/dist/src/sessions/adapters/deepseek-harness.js +178 -0
  62. package/dist/src/sessions/batch-import.js +7 -0
  63. package/dist/src/sessions/contracts.js +2 -1
  64. package/dist/src/sessions/index.js +1 -0
  65. package/dist/src/sessions/workspace-discovery.js +5 -1
  66. package/dist/src/skills/deployer.js +6 -6
  67. package/dist/src/smiths/context-pipeline/workspace-context.js +7 -0
  68. package/dist/src/writing/channel-packs.js +13 -0
  69. package/dist/src/writing/contextual-diagnostics.js +142 -0
  70. package/dist/src/writing/example-generator.js +7 -6
  71. package/dist/src/writing/exemplar-selection.js +186 -0
  72. package/dist/src/writing/fidelity.js +61 -0
  73. package/dist/src/writing/validation-engine.js +32 -15
  74. package/dist/src/writing/voice-evaluation.js +301 -0
  75. package/dist/src/writing/voice-revision.js +201 -0
  76. package/dist/src/writing/writer-migration.js +216 -0
  77. package/dist/src/writing/writer-profile-legacy.js +145 -0
  78. package/dist/src/writing/writer-profile-store.js +117 -0
  79. package/dist/src/writing/writer-profile.js +222 -0
  80. package/dist/src/writing/writing-brief.js +166 -0
  81. package/dist/src/writing/writing-channels.js +63 -0
  82. package/dist/src/writing/writing-consumer.js +39 -0
  83. package/dist/src/writing/writing-receipt.js +266 -0
  84. package/package.json +2 -1
  85. package/schemas/dataset/fortemi-live-qualification-receipt.v2.schema.json +196 -0
  86. package/schemas/dataset/fortemi-run-receipt/validation-1.0.1/authority.json +12 -0
  87. package/schemas/dataset/fortemi-run-receipt/validation-1.0.1/run-receipt.schema.json +819 -0
  88. package/tools/agents/deploy-agents.mjs +7 -3
  89. package/tools/agents/providers/antigravity.mjs +1 -1
  90. package/tools/agents/providers/base.mjs +3 -2
  91. package/tools/agents/providers/deepseek-harness.mjs +66 -0
  92. package/tools/agents/providers/hermes.mjs +1 -1
  93. package/tools/agents/providers/openhuman.mjs +2 -2
@@ -40,6 +40,22 @@ The schema is vendored unchanged to make UHP fixture and compatibility tests
40
40
  offline and version-pinned. AIWG's experimental client does not imply UHP
41
41
  server conformance.
42
42
 
43
+ ## Optional network-analysis tools
44
+
45
+ The `network-analysis` addon interoperates with separately installed tools. No
46
+ Wireshark, TShark, Capinfos, or Termshark executable or source is copied into the
47
+ AIWG npm archives.
48
+
49
+ - Wireshark project tools: TShark and Capinfos
50
+ - Upstream: <https://www.wireshark.org/download.html>
51
+ - Upstream license: GNU General Public License version 2 or later
52
+ - Termshark upstream: <https://github.com/gcla/termshark>
53
+ - Termshark license: MIT
54
+
55
+ Operators install and update these programs independently. AIWG discovers only
56
+ explicit executable paths or trusted search roots and does not change host
57
+ capture permissions.
58
+
43
59
  ## Distribution boundary
44
60
 
45
61
  The `aiwg` and `@aiwg/cli` npm archives do not copy Fortemi or Bytecask object code into their own tarballs. npm resolves those packages separately during installation. AIWG does, however, intentionally import Fortemi in-process at runtime, so separate archive delivery is not by itself a legal conclusion about whether execution forms a combined work.
@@ -370,7 +370,7 @@ providers:
370
370
  # bridge. The "MCP:" entries below are the optional enrichment path, not a
371
371
  # requirement. Broader MCP modernization: #1533.
372
372
  agents: AGENTS.md
373
- commands: "AGENTS.md bridge + aiwg discover/show (MCP command-run optional)"
373
+ commands: "Indexed: aiwg discover / aiwg show command (MCP command-run optional)"
374
374
  skills: "~/.hermes/skills/"
375
375
  skills_cross_agent: "~/.hermes/skills/.aiwg/"
376
376
  rules: "AGENTS.md ### Rule sections + aiwg show rule (MCP rule-list/rule-show optional)"
@@ -442,8 +442,8 @@ providers:
442
442
  daemon_tier: unsupported
443
443
  daemon_pty_adapter: false
444
444
  artifact_paths:
445
- agents: ".agents/agents/" # markdown personas (workspace-scoped the coding hosts OpenHuman drives); harness TOML is Tier-2 (#1559)
446
- commands: "AGENTS.md bridge (no native command surface)"
445
+ agents: "Indexed: aiwg discover / aiwg show agent" # native TOML harness agents are opt-in (#1559)
446
+ commands: "Indexed: aiwg discover / aiwg show command"
447
447
  skills: "~/.openhuman/skills/" # global/home-dir like OpenClaw — ungated user-scope native scan (ops_discover.rs); same root OpenHuman's own installer writes to (#1553)
448
448
  skills_cross_agent: "~/.openhuman/.aiwg/skills/"
449
449
  rules: "AGENTS.md ### Rule sections + aiwg show rule"
@@ -515,6 +515,48 @@ providers:
515
515
  deploy_target: project
516
516
  aggregated_output: false
517
517
 
518
+ deepseek-harness:
519
+ display_name: DeepSeek Harness
520
+ aliases:
521
+ - dsh
522
+ status: experimental
523
+ daemon_tier: unsupported
524
+ daemon_pty_adapter: false
525
+ artifact_paths:
526
+ agents: .agents/skills/
527
+ commands: null
528
+ skills: .agents/skills/
529
+ rules: AGENTS.md
530
+ behaviors: null
531
+ cordis_patch: .dsh/aiwg.cordis.patch.yml
532
+ native_features:
533
+ cron: false
534
+ agent_teams: true
535
+ tasks: true
536
+ mcp: true
537
+ behaviors: true
538
+ mission_control: false
539
+ daemon: false
540
+ emulation:
541
+ cron: external-trigger
542
+ agent_teams: native
543
+ tasks: native
544
+ mcp: native
545
+ behaviors: null
546
+ mission_control: aiwg-mc
547
+ daemon: null
548
+ hook_wiring:
549
+ at_link_support: false
550
+ fallback: full-inject
551
+ context_file: AGENTS.md
552
+ max_bytes: 65536
553
+ interaction:
554
+ structured_questions: none
555
+ fallback: markdown
556
+ notes: Headless and SDK profiles do not expose an interactive question UI.
557
+ deploy_target: project
558
+ aggregated_output: false
559
+
518
560
  pi:
519
561
  display_name: Pi Coding Agent
520
562
  aliases:
@@ -0,0 +1,8 @@
1
+ # DeepSeek Harness provider resources
2
+
3
+ AIWG deploys provider-neutral skills to `.agents/skills`, a compact `AGENTS.md`
4
+ bootstrap, and a project-owned Cordis patch at `.dsh/aiwg.cordis.patch.yml`.
5
+
6
+ The checked-in patch controls only AIWG's safe operational defaults. Runtime
7
+ provider routes and credential environment-variable references belong in a
8
+ separate, ephemeral patch generated by the transport adapter.
@@ -0,0 +1,10 @@
1
+ # AIWG-owned safe defaults for DeepSeek Harness. No secrets belong here.
2
+ - id: session-persistence-jsonl
3
+ config:
4
+ compression: none
5
+ - id: session-telemetry-otel
6
+ disabled: true
7
+ - id: sandbox-policy
8
+ config:
9
+ mode: workspace-write
10
+ workspaceRoot: !!js process.cwd()
@@ -118,6 +118,17 @@
118
118
  "verification": "Inspect strict JSONL state and the selected provider/model",
119
119
  "sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#cli-reference", "verifiedAt": "2026-09-04"
120
120
  },
121
+ "deepseek-harness": {
122
+ "agent": "native", "skill": "inherited", "globalChild": "native",
123
+ "identifierSyntax": "provider/model passed through the SDK initialize request or agent-default-model Cordis row",
124
+ "effortValues": ["minimal", "low", "medium", "high", "xhigh"],
125
+ "inheritance": "Subagents inherit the configured Harness route unless explicitly overridden",
126
+ "invalidPinFallback": "Explicit unavailable routes or models fail the request",
127
+ "configTarget": "Ephemeral Cordis route patch and SDK initialize request",
128
+ "artifactFormat": "agent-default-model config; JSON-RPC initialize params",
129
+ "verification": "Pinned source contract plus opt-in headless OpenRouter smoke",
130
+ "sourceUrl": "https://github.com/deepseek-ai/deepseek-harness", "verifiedAt": "2026-09-05"
131
+ },
121
132
  "windsurf": {
122
133
  "agent": "unsupported", "skill": "unsupported", "globalChild": "inherited",
123
134
  "identifierSyntax": "UI-selected provider model",
@@ -89,6 +89,14 @@
89
89
  },
90
90
  "sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#providers--models", "verifiedAt": "2026-09-04"
91
91
  },
92
+ "deepseek-harness": {
93
+ "roles": {
94
+ "reasoning": { "id": "configured/reasoning", "status": "unverified", "observed": false },
95
+ "coding": { "id": "configured/coding", "status": "unverified", "observed": false },
96
+ "efficiency": { "id": "configured/efficiency", "status": "unverified", "observed": false }
97
+ },
98
+ "sourceUrl": "https://github.com/deepseek-ai/deepseek-harness", "verifiedAt": "2026-09-05"
99
+ },
92
100
  "warp": {
93
101
  "roles": {
94
102
  "reasoning": { "id": "profile-selected", "status": "unverified", "observed": false },
package/bin/aiwg.mjs CHANGED
@@ -99,6 +99,7 @@ const FAST_HELP_TEXT = `
99
99
 
100
100
  WORKSPACE
101
101
  status Show workspace health and installed frameworks
102
+ sessions <command> Manage the normalized session catalog, imports, and analytics
102
103
  migrate-workspace Migrate legacy .aiwg/ to framework-scoped structure
103
104
  rollback-workspace Rollback workspace migration from backup
104
105
 
@@ -20,7 +20,21 @@ export * from '../uhp/index.js';
20
20
  export * from '../mission-protocol/index.js';
21
21
  export * from '../governance/index.js';
22
22
  export * from '../output-modes/index.js';
23
+ export * from '../writing/contextual-diagnostics.js';
23
24
  export * from '../schema/index.js';
24
25
  export * from '../dataset/index.js';
26
+ export * from '../network-analysis/index.js';
25
27
  export { ARTIFACT_TRUST_ROOT_MEDIA_TYPE, ARTIFACT_TRUST_ROOT_SCHEMA_VERSION, ARTIFACT_TRUST_STATE_SCHEMA_VERSION, bootstrapTrustRoot, parseTrustRoot, parseTrustState, readTrustState, validateTrustRoot, validateTrustState, verifyRootTransition, writeTrustState, channelStateKey, canonicalJson, dssePae, publicKeyFingerprint, sha256, type ArtifactTrustRoot, type ArtifactTrustState, type RootBootstrapResult, type RootTransitionResult, type ArtifactTrustPolicySettings, type TrustedChannelState, } from '../security/artifact-trust.js';
28
+ export * from '../writing/writer-profile.js';
29
+ export * from '../writing/writer-profile-store.js';
30
+ export * from '../writing/writer-profile-legacy.js';
31
+ export * from '../writing/exemplar-selection.js';
32
+ export * from '../writing/writing-brief.js';
33
+ export * from '../writing/fidelity.js';
34
+ export * from '../writing/voice-revision.js';
35
+ export * from '../writing/voice-evaluation.js';
36
+ export * from '../writing/writing-consumer.js';
37
+ export * from '../writing/writing-channels.js';
38
+ export * from '../writing/writing-receipt.js';
39
+ export * from '../writing/writer-migration.js';
26
40
  //# sourceMappingURL=index.d.ts.map
@@ -20,7 +20,21 @@ export * from '../uhp/index.js';
20
20
  export * from '../mission-protocol/index.js';
21
21
  export * from '../governance/index.js';
22
22
  export * from '../output-modes/index.js';
23
+ export * from '../writing/contextual-diagnostics.js';
23
24
  export * from '../schema/index.js';
24
25
  export * from '../dataset/index.js';
26
+ export * from '../network-analysis/index.js';
25
27
  export { ARTIFACT_TRUST_ROOT_MEDIA_TYPE, ARTIFACT_TRUST_ROOT_SCHEMA_VERSION, ARTIFACT_TRUST_STATE_SCHEMA_VERSION, bootstrapTrustRoot, parseTrustRoot, parseTrustState, readTrustState, validateTrustRoot, validateTrustState, verifyRootTransition, writeTrustState, channelStateKey, canonicalJson, dssePae, publicKeyFingerprint, sha256, } from '../security/artifact-trust.js';
28
+ export * from '../writing/writer-profile.js';
29
+ export * from '../writing/writer-profile-store.js';
30
+ export * from '../writing/writer-profile-legacy.js';
31
+ export * from '../writing/exemplar-selection.js';
32
+ export * from '../writing/writing-brief.js';
33
+ export * from '../writing/fidelity.js';
34
+ export * from '../writing/voice-revision.js';
35
+ export * from '../writing/voice-evaluation.js';
36
+ export * from '../writing/writing-consumer.js';
37
+ export * from '../writing/writing-channels.js';
38
+ export * from '../writing/writing-receipt.js';
39
+ export * from '../writing/writer-migration.js';
26
40
  //# sourceMappingURL=index.js.map
@@ -65,6 +65,7 @@ export const DEFAULT_SOURCE_TYPES = {
65
65
  audio: { description: 'Audio recording, lecture audio, or direct-hosted audio source.', aliases: ['audio', 'audio-recording', 'lecture-audio', 'recording-audio'], template: 'reference-media', requiredSections: ['Citation', 'Media Profile', 'Summary', 'Key Timestamps', 'Evidence and Transcript Method'], citationFormat: 'timestamp-transcript', acquisition: 'media-curator', storage: 'media/audio', qualityRules: 'media-grade', defaultRadarCadence: 'on-demand' },
66
66
  podcast: { description: 'Podcast episode or feed-hosted interview.', aliases: ['podcast', 'podcast-episode', 'episode', 'interview-audio'], template: 'reference-media', requiredSections: ['Citation', 'Media Profile', 'Summary', 'Key Timestamps', 'Evidence and Transcript Method'], citationFormat: 'timestamp-transcript', acquisition: 'media-curator', storage: 'media/audio', qualityRules: 'interview-hedged-grade', defaultRadarCadence: 'on-demand' },
67
67
  lecture: { description: 'Institutional lecture, course video, keynote, seminar, or invited talk recording.', aliases: ['lecture', 'course', 'seminar', 'keynote', 'talk', 'invited-talk', 'conference-talk'], template: 'reference-media', requiredSections: ['Citation', 'Media Profile', 'Summary', 'Key Timestamps', 'Evidence and Transcript Method'], citationFormat: 'timestamp-transcript', acquisition: 'media-curator', storage: 'media/video', qualityRules: 'lecture-grade', defaultRadarCadence: 'on-demand' },
68
+ 'packet-evidence': { description: 'Governed observational evidence derived from a saved network packet capture.', aliases: ['packet-evidence', 'packet evidence', 'pcap-evidence', 'network-trace', 'network trace'], template: 'reference-packet-evidence', requiredSections: ['Evidence Citation', 'Collection Context', 'Observed Facts', 'Inferences', 'Limitations', 'Provenance'], citationFormat: 'pcap-digest-frame-stream-time', acquisition: 'governed-evidence-bundle', storage: 'sources/evidence', qualityRules: 'observational-evidence-no-grade', defaultRadarCadence: 'on-demand' },
68
69
  'internal-review': { description: 'Internal research note or architecture review (not an external source).', aliases: ['internal-review', 'internal-research', 'internal-architecture-review', 'audit'], template: 'reference-internal', requiredSections: ['Summary', 'Findings'], citationFormat: 'internal-ref', acquisition: 'internal', storage: 'documentation/synthesis', qualityRules: 'internal-note', defaultRadarCadence: 'on-demand' },
69
70
  },
70
71
  };
@@ -11,6 +11,7 @@ import path from 'path';
11
11
  import { DEFAULT_INDEX_EXTENSIONS, GRAPH_CONFIGS, resolveGraphScanDir, } from './types.js';
12
12
  import { DEFAULT_PROJECT_AIWG_DIR, resolveProjectAiwgDir, } from '../config/project-artifacts.js';
13
13
  import { workspaceLinkedFiles } from '../smiths/context-pipeline/workspace-context.js';
14
+ import { discoverProjectLocalBundles } from '../extensions/project-local-discovery.js';
14
15
  function pathContains(parent, child) {
15
16
  const relative = path.relative(parent, child);
16
17
  return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
@@ -35,7 +36,7 @@ export function findArtifactFiles(dir, extensions = DEFAULT_INDEX_EXTENSIONS) {
35
36
  return walkArtifactFiles(dir, extensions, new Set());
36
37
  }
37
38
  /** Recursively find indexable files, excluding hidden directories such as .index. */
38
- function walkArtifactFiles(dir, extensions, seenRealDirs) {
39
+ function walkArtifactFiles(dir, extensions, seenRealDirs, boundary) {
39
40
  const results = [];
40
41
  if (!fs.existsSync(dir))
41
42
  return results;
@@ -55,6 +56,8 @@ function walkArtifactFiles(dir, extensions, seenRealDirs) {
55
56
  let stat;
56
57
  try {
57
58
  stat = fs.statSync(fullPath);
59
+ if (boundary && !pathContains(boundary, fs.realpathSync(fullPath)))
60
+ continue;
58
61
  }
59
62
  catch {
60
63
  continue;
@@ -62,7 +65,7 @@ function walkArtifactFiles(dir, extensions, seenRealDirs) {
62
65
  if (stat.isDirectory()) {
63
66
  if (entry.name.startsWith('.'))
64
67
  continue;
65
- results.push(...walkArtifactFiles(fullPath, extensions, seenRealDirs));
68
+ results.push(...walkArtifactFiles(fullPath, extensions, seenRealDirs, boundary));
66
69
  }
67
70
  else if (stat.isFile() && extensions.some(extension => entry.name.endsWith(extension))) {
68
71
  results.push(fullPath);
@@ -86,6 +89,18 @@ export async function collectGraphIndexFiles(cwd, graph) {
86
89
  files.add(file);
87
90
  }
88
91
  if (!graph || graph === 'project') {
92
+ // Additional search roots authorize bundle payloads, not the surrounding
93
+ // external corpus. Reuse deployment's validated source resolution, including
94
+ // plugin payloads, and prevent payload symlinks from escaping that boundary.
95
+ const { bundles } = await discoverProjectLocalBundles(cwd);
96
+ for (const bundle of bundles) {
97
+ if (scanDirs.some(scanDir => pathContains(scanDir, bundle.artifactPath)))
98
+ continue;
99
+ const boundary = fs.realpathSync(bundle.artifactPath);
100
+ for (const file of walkArtifactFiles(bundle.artifactPath, extensions, new Set(), boundary)) {
101
+ files.add(file);
102
+ }
103
+ }
89
104
  const workspacePath = path.join(cwd, 'WORKSPACE.md');
90
105
  const contextFiles = [
91
106
  ...(fs.existsSync(workspacePath) ? [workspacePath] : []),
@@ -1,10 +1,30 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import { PROJECT_CONTROL_PLANE_FILES, auditProjectArtifactHealth, } from '../config/project-artifacts.js';
5
+ async function optionalBytes(filePath) {
6
+ try {
7
+ return await readFile(filePath);
8
+ }
9
+ catch (error) {
10
+ if (error.code === 'ENOENT')
11
+ return null;
12
+ throw error;
13
+ }
14
+ }
15
+ async function writeAndVerify(destination, bytes) {
16
+ await mkdir(path.dirname(destination), { recursive: true });
17
+ await writeFile(destination, bytes, { flag: 'wx' });
18
+ if (!(await readFile(destination)).equals(bytes)) {
19
+ throw new Error(`Byte verification failed after copying artifact payload: ${destination}`);
20
+ }
21
+ }
4
22
  export async function repairProjectArtifacts(options) {
5
23
  const projectDir = path.resolve(options.projectDir);
6
24
  const before = auditProjectArtifactHealth(projectDir);
7
25
  const copied = [];
26
+ const migrated = [];
27
+ const archivedConflicts = [];
8
28
  const removed = [];
9
29
  if (!before.external_configured) {
10
30
  throw new Error('No external AIWG artifact corpus is configured for this project.');
@@ -12,11 +32,8 @@ export async function repairProjectArtifacts(options) {
12
32
  if (!before.external_reachable) {
13
33
  throw new Error(`External AIWG artifact corpus is unavailable: ${before.artifact_root}`);
14
34
  }
15
- if (before.divergent_control_files.length || before.divergent_local_corpus_files.length) {
16
- throw new Error(`Automatic repair refused because local and external content diverges: ${[
17
- ...before.divergent_control_files,
18
- ...before.divergent_local_corpus_files,
19
- ].join(', ')}`);
35
+ if (before.divergent_control_files.length) {
36
+ throw new Error(`Automatic repair refused because local and external control-plane content diverges: ${before.divergent_control_files.join(', ')}`);
20
37
  }
21
38
  for (const relativePath of before.missing_local_control_files) {
22
39
  const source = path.join(before.artifact_root, relativePath);
@@ -31,9 +48,39 @@ export async function repairProjectArtifacts(options) {
31
48
  }
32
49
  }
33
50
  for (const relativePath of before.duplicated_local_corpus_files) {
51
+ const localPath = path.join(before.local_control_root, relativePath);
52
+ const externalPath = path.join(before.artifact_root, relativePath);
53
+ const localBytes = await readFile(localPath);
54
+ const externalBytes = await optionalBytes(externalPath);
55
+ if (externalBytes === null) {
56
+ migrated.push(relativePath);
57
+ if (options.apply)
58
+ await writeAndVerify(externalPath, localBytes);
59
+ }
60
+ else if (!externalBytes.equals(localBytes)) {
61
+ const digest = createHash('sha256').update(localBytes).digest('hex').slice(0, 12);
62
+ const archiveRelative = path.join('archive', 'local-corpus-migration', 'conflicts', 'local', `${relativePath}.${digest}`);
63
+ archivedConflicts.push(archiveRelative.split(path.sep).join('/'));
64
+ if (options.apply) {
65
+ const archivePath = path.join(before.artifact_root, archiveRelative);
66
+ const archived = await optionalBytes(archivePath);
67
+ if (archived === null)
68
+ await writeAndVerify(archivePath, localBytes);
69
+ else if (!archived.equals(localBytes))
70
+ throw new Error(`Conflict archive path already contains different bytes: ${archivePath}`);
71
+ }
72
+ }
34
73
  removed.push(relativePath);
35
74
  if (options.apply) {
36
- await rm(path.join(before.local_control_root, relativePath), { force: false });
75
+ const preservedPath = externalBytes === null
76
+ ? externalPath
77
+ : externalBytes.equals(localBytes)
78
+ ? externalPath
79
+ : path.join(before.artifact_root, archivedConflicts.at(-1));
80
+ if (!(await readFile(preservedPath)).equals(localBytes)) {
81
+ throw new Error(`Refusing to remove local payload before byte verification: ${relativePath}`);
82
+ }
83
+ await rm(localPath, { force: false });
37
84
  }
38
85
  }
39
86
  return {
@@ -41,6 +88,8 @@ export async function repairProjectArtifacts(options) {
41
88
  before,
42
89
  after: options.apply ? auditProjectArtifactHealth(projectDir) : before,
43
90
  copied,
91
+ migrated,
92
+ archivedConflicts,
44
93
  removed,
45
94
  };
46
95
  }
@@ -192,6 +192,26 @@ export async function handleCatalogSearch(args) {
192
192
  console.log(`Search results for: "${query}"`);
193
193
  displayModelsTable(models);
194
194
  }
195
+ export function printCatalogHelp() {
196
+ console.log(`Usage: aiwg catalog <command> [options]
197
+
198
+ Commands:
199
+ list List models with optional filters
200
+ info <id> Show detailed model information
201
+ search <query> Search models by query
202
+
203
+ Options (list):
204
+ --provider <id> Filter by provider
205
+ --status <status> Filter by model status
206
+ --tag <tag> Filter by tag
207
+ --min-context <n> Minimum context window in tokens
208
+
209
+ Examples:
210
+ aiwg catalog list
211
+ aiwg catalog list --provider anthropic
212
+ aiwg catalog info claude-opus-4-6
213
+ aiwg catalog search opus`);
214
+ }
195
215
  /**
196
216
  * Main catalog command router
197
217
  */
@@ -210,13 +230,7 @@ export async function main(args) {
210
230
  break;
211
231
  case undefined:
212
232
  console.error('Error: Catalog subcommand required');
213
- console.log('Available: list, info, search');
214
- console.log('');
215
- console.log('Examples:');
216
- console.log(' aiwg catalog list');
217
- console.log(' aiwg catalog list --provider anthropic');
218
- console.log(' aiwg catalog info claude-opus-4-6');
219
- console.log(' aiwg catalog search opus');
233
+ printCatalogHelp();
220
234
  process.exit(1);
221
235
  break;
222
236
  default:
@@ -249,6 +249,27 @@ export async function handleCatalogSearch(args) {
249
249
  displayModelsTable(models);
250
250
  }
251
251
 
252
+ export function printCatalogHelp() {
253
+ console.log(`Usage: aiwg catalog <command> [options]
254
+
255
+ Commands:
256
+ list List models with optional filters
257
+ info <id> Show detailed model information
258
+ search <query> Search models by query
259
+
260
+ Options (list):
261
+ --provider <id> Filter by provider
262
+ --status <status> Filter by model status
263
+ --tag <tag> Filter by tag
264
+ --min-context <n> Minimum context window in tokens
265
+
266
+ Examples:
267
+ aiwg catalog list
268
+ aiwg catalog list --provider anthropic
269
+ aiwg catalog info claude-opus-4-6
270
+ aiwg catalog search opus`);
271
+ }
272
+
252
273
  /**
253
274
  * Main catalog command router
254
275
  */
@@ -271,13 +292,7 @@ export async function main(args) {
271
292
 
272
293
  case undefined:
273
294
  console.error('Error: Catalog subcommand required');
274
- console.log('Available: list, info, search');
275
- console.log('');
276
- console.log('Examples:');
277
- console.log(' aiwg catalog list');
278
- console.log(' aiwg catalog list --provider anthropic');
279
- console.log(' aiwg catalog info claude-opus-4-6');
280
- console.log(' aiwg catalog search opus');
295
+ printCatalogHelp();
281
296
  process.exit(1);
282
297
  break;
283
298
 
@@ -38,6 +38,12 @@ export const PROVIDER_CONFIGS = {
38
38
  dangerousFlag: '--dangerously-bypass-approvals-and-sandbox',
39
39
  name: 'OpenAI Codex',
40
40
  },
41
+ 'deepseek-harness': {
42
+ binary: 'dsh',
43
+ dangerousFlag: null,
44
+ name: 'DeepSeek Harness',
45
+ promptPrefix: ['--profile', 'headless'],
46
+ },
41
47
  hermes: {
42
48
  // Hermes is a model series (NousResearch), not a confirmed standalone CLI.
43
49
  // Treat as IDE/runtime-integrated until a CLI is confirmed.
@@ -157,7 +163,10 @@ export function splitParams(params) {
157
163
  return result;
158
164
  }
159
165
  // ── Provider helpers ──────────────────────────────────────────
160
- const SPAWN_PROVIDER_ALIASES = { agy: 'antigravity' };
166
+ const SPAWN_PROVIDER_ALIASES = {
167
+ agy: 'antigravity',
168
+ dsh: 'deepseek-harness',
169
+ };
161
170
  /** Get config for a provider. Unknown values fail closed instead of launching another harness. */
162
171
  export function getProviderConfig(provider) {
163
172
  const candidate = provider.trim().toLowerCase();
@@ -1,13 +1,13 @@
1
1
  import { getProjectDir } from '../../config/aiwg-config.js';
2
2
  import { moveProjectArtifacts } from '../../artifacts/move.js';
3
3
  import { repairProjectArtifacts } from '../../artifacts/repair.js';
4
- import { resolveProjectAiwgDir } from '../../config/project-artifacts.js';
4
+ import { isProjectArtifactRootExternal, resolveProjectAiwgDir, resolveProjectAiwgDirForWrite, } from '../../config/project-artifacts.js';
5
5
  function usage() {
6
6
  return [
7
7
  'aiwg artifacts — Manage the project AIWG artifact root',
8
8
  '',
9
9
  'Usage:',
10
- ' aiwg artifacts path [--json]',
10
+ ' aiwg artifacts path [--json] [--check-write]',
11
11
  ' aiwg artifacts move --to <path> [--from <path>] [--dry-run] [--no-reindex] [--no-sync]',
12
12
  ' aiwg artifacts attach --to <existing-path> [--dry-run] [--no-reindex] [--no-sync]',
13
13
  ' aiwg artifacts repair --dry-run',
@@ -43,6 +43,19 @@ export const artifactsHandler = {
43
43
  if (action === 'path') {
44
44
  const projectDir = getProjectDir(ctx, ctx.args);
45
45
  const artifactRoot = resolveProjectAiwgDir(projectDir);
46
+ const external = isProjectArtifactRootExternal(projectDir);
47
+ let writeReady = true;
48
+ let writeError = null;
49
+ try {
50
+ resolveProjectAiwgDirForWrite(projectDir);
51
+ }
52
+ catch (error) {
53
+ writeReady = false;
54
+ writeError = error instanceof Error ? error.message : String(error);
55
+ }
56
+ if (ctx.args.includes('--check-write') && !writeReady) {
57
+ return { exitCode: 1, message: writeError ?? `Artifact root is unavailable: ${artifactRoot}` };
58
+ }
46
59
  if (ctx.args.includes('--json')) {
47
60
  return {
48
61
  exitCode: 0,
@@ -50,6 +63,10 @@ export const artifactsHandler = {
50
63
  schema: 'aiwg.artifacts.path.v1',
51
64
  project_root: projectDir,
52
65
  artifact_root: artifactRoot,
66
+ external,
67
+ write_ready: writeReady,
68
+ write_error: writeError,
69
+ local_control_files: ['AIWG.md', 'aiwg.config', 'frameworks/registry.json'],
53
70
  }, null, 2),
54
71
  rawOutput: true,
55
72
  };
@@ -70,7 +87,9 @@ export const artifactsHandler = {
70
87
  ` Local control plane: ${result.before.local_control_root}`,
71
88
  ` External corpus: ${result.before.artifact_root}`,
72
89
  ` Copy locally: ${result.copied.length ? result.copied.join(', ') : 'none'}`,
73
- ` Remove local identical corpus copies: ${result.removed.length ? result.removed.join(', ') : 'none'}`,
90
+ ` Migrate local-only payload: ${result.migrated.length ? result.migrated.join(', ') : 'none'}`,
91
+ ` Archive divergent local variants: ${result.archivedConflicts.length ? result.archivedConflicts.join(', ') : 'none'}`,
92
+ ` Remove local payload after verified preservation: ${result.removed.length ? result.removed.join(', ') : 'none'}`,
74
93
  ` Result: ${result.after.classification}`,
75
94
  applied ? '' : 'No files changed. Re-run with --apply after reviewing this plan.',
76
95
  ].filter(Boolean).join('\n'),
@@ -60,6 +60,7 @@ function displayHelp() {
60
60
  ]);
61
61
  helpGroup('WORKSPACE', [
62
62
  ['status', 'Show workspace health and installed frameworks'],
63
+ ['sessions <command>', 'Manage the normalized session catalog, imports, and analytics'],
63
64
  ['migrate-workspace', 'Migrate legacy .aiwg/ to framework-scoped structure'],
64
65
  ['rollback-workspace', 'Rollback workspace migration from backup'],
65
66
  ]);
@@ -91,6 +92,8 @@ function displayHelp() {
91
92
  helpGroup('DISPATCH', [
92
93
  ['run skill <name>', 'Execute a script-bearing skill'],
93
94
  ['run <script-name>', 'Run a user-defined script from .aiwg/aiwg.config'],
95
+ ['writing <plan|proofread>', 'Use grounded briefs and authorized proofreading'],
96
+ ['writer-profile <action>', 'Manage author-controlled writer profile sidecars'],
94
97
  ['output-mode <action>', 'Configure composable output language and presentation'],
95
98
  ]);
96
99
  helpGroup('FEATURES', [
@@ -63,6 +63,8 @@ import { jobHandler } from './job.js';
63
63
  import { costReportHandler } from './cost-report.js';
64
64
  import { evidenceHandler } from './evidence.js';
65
65
  import { artifactVerifyHandler } from './artifact-verify.js';
66
+ import { writingHandler } from './writing.js';
67
+ import { writerProfileHandler } from './writer-profile.js';
66
68
  import { outputModeHandler } from './output-mode.js';
67
69
  import { installationHandler } from './installation.js';
68
70
  import { uhpHandler } from './uhp.js';
@@ -73,7 +75,7 @@ export {
73
75
  // Maintenance
74
76
  helpHandler, versionHandler, authHandler, doctorHandler, contextFirewallHandler, updateHandler, refreshHandler, regenerateHandler, workspaceContextHandler,
75
77
  // Framework management
76
- useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler, artifactVerifyHandler, outputModeHandler, schemaHandler, datasetHandler, installationHandler,
78
+ useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler, artifactVerifyHandler, outputModeHandler, writerProfileHandler, writingHandler, schemaHandler, datasetHandler, installationHandler,
77
79
  // Project
78
80
  newBundleHandler, quickrefHandler, newProjectHandler, sessionHandler, sessionsHandler,
79
81
  // Workspace
@@ -158,6 +160,8 @@ export const allHandlers = [
158
160
  evidenceHandler,
159
161
  artifactVerifyHandler,
160
162
  outputModeHandler,
163
+ writerProfileHandler,
164
+ writingHandler,
161
165
  schemaHandler,
162
166
  datasetHandler,
163
167
  // Workspace management
@@ -10,8 +10,8 @@ import { load as loadYaml } from 'js-yaml';
10
10
  import { compileModelPolicy, validateCanonicalModelPolicy, validateUserProjectModelConfig, } from '../../models/provider-policy.js';
11
11
  const TIERS = new Set(['economy', 'standard', 'premium', 'max-quality']);
12
12
  const PROVIDERS = new Set([
13
- 'antigravity', 'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
14
- 'opencode', 'openclaw', 'openhuman', 'warp', 'windsurf',
13
+ 'antigravity', 'claude', 'codex', 'copilot', 'cursor', 'deepseek-harness', 'factory', 'hermes',
14
+ 'opencode', 'openclaw', 'openhuman', 'omp', 'pi', 'warp', 'windsurf',
15
15
  ]);
16
16
  function parseArgs(args) {
17
17
  const [subcommand = 'help', ...rest] = args;
@@ -30,7 +30,7 @@ async function execute(ctx) {
30
30
  if (resolved.modes.length === 0)
31
31
  return { exitCode: 0, rawOutput: true, message: 'Effective output mode: unaltered\nContext cost: 0 tokens\nNo transformations active.' };
32
32
  const lines = resolved.modes.map((p, i) => `${i + 1}. ${p.id} [${p.kind}/${p.stage}] source=${p.source} scope=${p.scope} validation=${p.validation.level} context≈${p.contextCost ?? 0}`);
33
- return { exitCode: 0, rawOutput: true, message: `Effective ordered stack:\n${lines.join('\n')}\nEstimated context cost: ${resolved.modes.reduce((n, p) => n + (p.contextCost ?? 0), 0)} tokens` };
33
+ return { exitCode: 0, rawOutput: true, message: `Selected ordered stack (no provider interception):\n${lines.join('\n')}\nDelivered/applied/validated: not observed by this status command.\nEstimated context cost: ${resolved.modes.reduce((n, p) => n + (p.contextCost ?? 0), 0)} tokens` };
34
34
  }
35
35
  if (!['enable', 'disable', 'clear'].includes(action))
36
36
  return { exitCode: 1, message: `Unknown output-mode action '${action}'.` };
@@ -178,7 +178,7 @@ async function handleRuntimeInfo(args, cwd = process.cwd()) {
178
178
  console.log(`Aggregated output: ${caps.aggregated_output}`);
179
179
  console.log(`\nArtifact paths:`);
180
180
  for (const [type, path] of Object.entries(caps.artifact_paths)) {
181
- console.log(` ${type}: ${path ?? '(none)'}`);
181
+ console.log(` ${type}: ${path || 'Indexed (aiwg discover / aiwg show)'}`);
182
182
  }
183
183
  console.log(`\nFeatures:`);
184
184
  for (const [feat, native] of Object.entries(caps.native_features)) {